#include "Utility.hpp"
#include "SearchablePDFPlugin.hpp"
#include <stdlib.h>
#include <utils/PathUtils.h>
#include <utils/ESFile.h>
#include <sys/wait.h>
namespace epsonscan
{

class WriteMessageQ
{ 
 public:
      WriteMessageQ()
      {
      }

      void Init()
      {
          
        ESString workFolder = ES_CMN_FUNCS::PATH::ES_GetWorkTempPath();

        auto inFile = CESFile::CreateTempFileInstanceWithPrefix(workFolder,  "WriteMessageQ", CESFile::ES_OPEN_MODE_WRITE_PLUS);
        if (inFile == nullptr)
        {
            SDI_TRACE_LOG("DTRInput file creates error");
            return;
        }
        remove(inFile->GetFileName().c_str());
        if(mkfifo(inFile->GetFileName().c_str(), 0666) != 0)
        {
            SDI_TRACE_LOG(ES_STRING("mkfifo %s"), ESString(strerror(errno)).c_str());
        }
        SDI_TRACE_LOG("pipe path = %s", inFile->GetFileName().c_str());
        pipePath_ = inFile->GetFileName();

        delete inFile;
        inFile = nullptr;
      }

      void Open()
      {
        writeDescripter_ = fopen(pipePath_.c_str(), "w");
        if (writeDescripter_ == nullptr)
        {
            throw std::runtime_error("fails to open read pipe");
        }     
      }

      void SendImage(std::string filename, int width, int height, int resolution, int dataType)
      {
          SDI_TRACE_LOG("IMAGE %s %d %d %d %d\n", filename.c_str(), width, height, resolution, dataType );

          fprintf(writeDescripter_,"IMAGE %s %d %d %d %d\n", filename.c_str(), width, height, resolution, dataType );
          fflush(writeDescripter_);
      }

      void SendFin()
      {
          fprintf(writeDescripter_,"FINISH\n"); 
          fflush(writeDescripter_);
      }

      virtual ~WriteMessageQ()
      {
          EndQ();
      }

      void EndQ()
      {
          if (writeDescripter_ != 0)
          {
            fclose(writeDescripter_);
            writeDescripter_ = nullptr;
            remove(pipePath_.c_str());
          }        
      }

      std::string GetPath()
      {
          return pipePath_;
      }

  private:
      FILE* writeDescripter_;
      std::string pipePath_;
};

static std::string GetPluginRootDir()
{
	  std::string strModulePath = EPSON_INSTALL_PATH;
      ES_CMN_FUNCS::PATH::ES_CombinePath(strModulePath, strModulePath, ES_STRING("non-free-exec"));
	  return strModulePath;
}

static ESString GetPDFPluginPath()
{
    std::string pluginRoot = GetPluginRootDir();
    ESString pdfplugin;
    ES_CMN_FUNCS::PATH::ES_CombinePath(pdfplugin, pluginRoot, ES_STRING("pdfplugin"));
    return pdfplugin;
}

static ESString GetPDFLibPath()
{
    std::string pluginRoot = GetPluginRootDir();
    ESString pdfplugin;
    ES_CMN_FUNCS::PATH::ES_CombinePath(pdfplugin, pluginRoot, ES_STRING("libpdflib.so"));
    return pdfplugin;
}
static ESString GetOCRCompPath()
{
    return EPSON_OCR_INSTALL_PATH;
}

bool SearchablePDFPlugin::IsAvailable()
{
    ESString pdfPlugin = GetPDFPluginPath();
    SDI_TRACE_LOG("pdfPlugin = %s", pdfPlugin.c_str());

    ESString pdfLibPath = GetPDFLibPath();
    SDI_TRACE_LOG("pdfLibPath = %s", pdfLibPath.c_str());

    if(ES_CMN_FUNCS::PATH::ES_IsExistFile(pdfPlugin) == false || ES_CMN_FUNCS::PATH::ES_IsExistFile(pdfLibPath) == false)
    {
        SDI_TRACE_LOG("pdfLib does not exists");
        return false;
    }

    return true;
}


bool SearchablePDFPlugin::Start(const char* destPath, std::shared_ptr<KeyMgr> keyMgr,  ESNumber format)
{
    SDI_TRACE_LOG("Enter");
    try{

        inputFileList_.clear();

        writeMessageQ_ = std::make_shared<WriteMessageQ>();
        writeMessageQ_->Init();

        if(IsAvailable() == false)
        {
            SDI_TRACE_LOG("pdfLib does not exists");
            return false;
        }

        ESString pdfPlugin = GetPDFPluginPath();
        SDI_TRACE_LOG("pdfPlugin = %s", pdfPlugin.c_str());


        int isOCR = 0;
        if (format == kSDIImageFormatSearchablePDF)
        {
            isOCR = 1;
        }

        int isPDFA = keyMgr->GetValueInt(kSDIPDFAEnabledKey);
        int isColor = 1;

        ESNumber colorType = keyMgr->GetValueInt(kSDIColorTypeKey);
        if (colorType == kSDIColorTypeAuto || colorType == kSDIColorTypeRGB24)
        {
            isColor = 1;
        }else{
            isColor = 0;
        }
        SDI_TRACE_LOG("%s %s %s %s %s %d %d %d", pdfPlugin.c_str(),  GetPDFLibPath().c_str(), destPath, writeMessageQ_->GetPath().c_str(), GetOCRCompPath().c_str(), isPDFA, isOCR, isColor);

        int pid = fork();
        if (pid == 0)
        {
            // child process

            if(execl(pdfPlugin.c_str(),pdfPlugin.c_str(),  GetPDFLibPath().c_str(), destPath ,  writeMessageQ_->GetPath().c_str(), GetOCRCompPath().c_str(), std::to_string(isPDFA).c_str(), std::to_string(isOCR).c_str(), std::to_string(isColor).c_str(), NULL)  < 0){
                SDI_TRACE_LOG(ES_STRING("%s[%xd] : %s"), ESString(pdfPlugin).c_str(), getpid(), ESString(strerror(errno)).c_str());
            }

            exit(0);
        }
        procesPid_ = pid;

        writeMessageQ_->Open();
    }catch(...)
    {
        SDI_TRACE_LOG("pdfPlugin open error");
        End();
        return false;
    }
    return true;
}

bool SearchablePDFPlugin::Add(ES_CMN_FUNCS::BUFFER::CESHeapBuffer dataBuf, int width, int height, int resolution, int samplesPerPixel, int bitsPerSample)
{
    std::string pluginRoot = GetPluginRootDir();

    SDI_TRACE_LOG("pdfPlugin Exists");

    try{
        ESString workFolder = ES_CMN_FUNCS::PATH::ES_GetWorkTempPath();

        auto inFile = CESFile::CreateTempFileInstanceWithPrefix(workFolder,  "PDFInput", CESFile::ES_OPEN_MODE_WRITE_PLUS);
        if (inFile == nullptr)
        {
            SDI_TRACE_LOG("PDFInput file creates error");
            End();
            return kSDIErrorNoMemory;
        }

        if(inFile->WriteData(dataBuf) == false)
        {
            SDI_TRACE_LOG("WriteData fails");

            if(ES_CMN_FUNCS::PATH::ES_IsExistFile(inFile->GetFileName()))
            {
                remove(inFile->GetFileName().c_str());
            }

            delete inFile;
            inFile = nullptr;
                        
            End();
            return kSDIErrorWriteFile;
        }

        inFile->Flush();
        inFile->CloseFile();    

        inputFileList_.push_back(inFile->GetFileName());

        if (samplesPerPixel == 3)   //RGB
        {    
            writeMessageQ_->SendImage(inFile->GetFileName(), width, height, resolution, 0);
        }
        else if (samplesPerPixel == 1 && bitsPerSample == 8) // GRAY
        {    
            writeMessageQ_->SendImage(inFile->GetFileName(), width, height, resolution, 1);
        }
        else if (samplesPerPixel == 1 && bitsPerSample == 1) // MONO 
        {    
            writeMessageQ_->SendImage(inFile->GetFileName(), width, height, resolution, 2);
        }
        else{
            delete inFile;
            inFile = nullptr;

            End();
            return false;
        }

        delete inFile;
        inFile = nullptr;

    }catch(...)
    {
        SDI_TRACE_LOG("pdfPlugin add error");
        End();
        return false;     
    }
    return true;
}

bool SearchablePDFPlugin::Fin()
{
    writeMessageQ_->SendFin();
    SDI_TRACE_LOG("pdf creation ok");  
       
    siginfo_t siginfo;
    waitid(P_PID, procesPid_, &siginfo, WEXITED);

    Cleanup();

    return true;
}   

void SearchablePDFPlugin::Cleanup()                                                                                                      
{
    for (auto value : inputFileList_)
    {
        if(ES_CMN_FUNCS::PATH::ES_IsExistFile(value))
        {
            remove(value.c_str());
        }
    }

    if(ES_CMN_FUNCS::PATH::ES_IsExistFile(writeMessageQ_->GetPath()))
    {
        remove(writeMessageQ_->GetPath().c_str());
    }
}

void SearchablePDFPlugin::End()                                                                                                      
{
    SDI_TRACE_LOG("FIN");
    int pid = 0; 

    writeMessageQ_->EndQ();

    if (0 != kill (procesPid_, SIGHUP)) {
        SDI_TRACE_LOG(ES_STRING("kill %s"), ESString(strerror(errno)).c_str());
    }

    siginfo_t siginfo;
    waitid(P_PID, procesPid_, &siginfo, WEXITED);

    Cleanup();
}

}