/*
TMPPANEL.CPP

Temporary panel main plugin code

*/

#include "TmpPanel.hpp"
#include <sys/stat.h>
#include <sys/mman.h>

TCHAR *PluginRootKey;
unsigned int CurrentCommonPanel;
struct PluginStartupInfo Info;
struct FarStandardFunctions FSF;

PluginPanels CommonPanels[COMMONPANELSNUMBER];

#if defined(__GNUC__)

#ifdef __cplusplus
extern "C"{
#endif
  BOOL WINAPI DllMainCRTStartup(HANDLE hDll,DWORD dwReason,LPVOID lpReserved);
#ifdef __cplusplus
};
#endif

BOOL WINAPI DllMainCRTStartup(HANDLE hDll,DWORD dwReason,LPVOID lpReserved)
{
  (void) lpReserved;
  (void) dwReason;
  (void) hDll;
  return TRUE;
}
#endif

#ifndef UNICODE
#define WITH_ANSI_ARG     ,ANSI
#define WITH_ANSI_PARAM   ,int ANSI
#else
#define WITH_ANSI_ARG
#define WITH_ANSI_PARAM
#endif
static void ProcessList(HANDLE hPlugin, TCHAR *Name, int Mode WITH_ANSI_PARAM);
static void ShowMenuFromList(TCHAR *Name);
static HANDLE OpenPanelFromOutput (TCHAR *argv WITH_ANSI_PARAM);

static TCHAR TmpPanelPath[] = WGOOD_SLASH _T("TmpPanel");
static wchar_t *TmpPanelModule = nullptr;

const wchar_t *GetTmpPanelModule()
{
	return TmpPanelModule;
}

SHAREDSYMBOL void WINAPI EXP_NAME(SetStartupInfo)(const struct PluginStartupInfo *Info)
{
  ::Info=*Info;
  ::FSF=*Info->FSF;
  ::Info.FSF=&::FSF;
  TmpPanelModule = wcsdup(Info->ModuleName);

  PluginRootKey = (TCHAR *)malloc((lstrlen(Info->RootKey) + 1) * sizeof(TCHAR) + sizeof(TmpPanelPath));
  lstrcpy(PluginRootKey,Info->RootKey);
  lstrcat(PluginRootKey, TmpPanelPath);
  GetOptions();
  StartupOptFullScreenPanel=Opt.FullScreenPanel;
  StartupOptCommonPanel=Opt.CommonPanel;
  CurrentCommonPanel=0;
  memset(CommonPanels, 0, sizeof(CommonPanels));
  CommonPanels[0].Items=(PluginPanelItem*)calloc(1,sizeof(PluginPanelItem));
  Opt.LastSearchResultsPanel = 0;
}


SHAREDSYMBOL HANDLE WINAPI EXP_NAME(OpenPlugin)(int OpenFrom,INT_PTR Item)
{
  HANDLE hPlugin=INVALID_HANDLE_VALUE;

  GetOptions();

  StartupOpenFrom=OpenFrom;
  if (OpenFrom==OPEN_COMMANDLINE)
  {
    TCHAR *argv=(TCHAR*)Item;
#ifndef UNICODE
    int ANSI = FALSE;
    #define OPT_COUNT 6
#else
    #define OPT_COUNT 5
#endif
    static const TCHAR ParamsStr[OPT_COUNT][8]=
    {
#ifndef UNICODE
      _T("ansi"),
#endif
      _T("safe"),_T("any"),_T("replace"),_T("menu"),_T("full")};
    const int *ParamsOpt[OPT_COUNT]=
    {
#ifndef UNICODE
      &ANSI,
#endif
      &Opt.SafeModePanel,&Opt.AnyInPanel,&Opt.Mode,
      &Opt.MenuForFilelist,&Opt.FullScreenPanel
    };

    while (argv && *argv==_T(' '))
      argv++;

    while (lstrlen(argv)>1 && (*argv==_T('+') || *argv==_T('-')))
    {
      int k=0;
      while (argv && *argv!=_T(' ') && *argv!=_T('<'))
      {
        k++;
        argv++;
      }

      StrBuf tmpTMP(k+1);
      TCHAR *TMP=tmpTMP;
      lstrcpyn(TMP,argv-k,k+1);

      for (int i=0;i<OPT_COUNT;i++)
        if (lstrcmpi(TMP+1,ParamsStr[i])==0)
          *(int*)ParamsOpt[i] = *TMP==_T('+');

      if (*(TMP+1)>=_T('0') && *(TMP+1)<=_T('9'))
        CurrentCommonPanel=*(TMP+1)-_T('0');

      while(argv && *argv==_T(' '))
        argv++;
    }

    FSF.Trim(argv);
    if (lstrlen(argv))
    {
      if (*argv==_T('<'))
      {
        argv++;
        hPlugin = OpenPanelFromOutput (argv WITH_ANSI_ARG);
        if (Opt.MenuForFilelist)
          return INVALID_HANDLE_VALUE;
      }
      else
      {
        FSF.Unquote(argv);
        StrBuf TmpIn;
        ExpandEnvStrs(argv,TmpIn);
        StrBuf TmpOut;
        if (FindListFile(TmpIn,TmpOut))
        {
          if (Opt.MenuForFilelist)
          {
            ShowMenuFromList(TmpOut);
            return INVALID_HANDLE_VALUE;
          }
          else
          {
            hPlugin=new TmpPanel();
            if (hPlugin==NULL)
              return INVALID_HANDLE_VALUE;

            ProcessList(hPlugin, TmpOut, Opt.Mode WITH_ANSI_ARG);
          }
        }
        else
        {
          return INVALID_HANDLE_VALUE;
        }
      }
    }
  }

  if (hPlugin==INVALID_HANDLE_VALUE)
  {
    hPlugin=new TmpPanel();
    if (hPlugin==NULL)
      return INVALID_HANDLE_VALUE;
  }

  return hPlugin;
}

static HANDLE OpenPanelFromOutput (TCHAR *argv WITH_ANSI_PARAM)
{
  TCHAR *tempDir=ParseParam(argv);

  BOOL allOK=FALSE;

  StrBuf tempfilename(NT_MAX_PATH); //BUGBUG
  StrBuf cmdparams(NT_MAX_PATH); //BUGBUG
  StrBuf fullcmd;

  FSF.MkTemp(tempfilename,
#ifdef UNICODE
             tempfilename.Size(),
#endif
             _T("FARTMP"));

  lstrcpy(cmdparams,_T("%COMSPEC% /c "));
  lstrcat(cmdparams,argv);
  ExpandEnvStrs(cmdparams,fullcmd);

  /*SECURITY_ATTRIBUTES sa;
  memset(&sa, 0, sizeof(sa));
  sa.nLength=sizeof(sa);
  sa.bInheritHandle=TRUE;*/

  HANDLE FileHandle;
  FileHandle=CreateFile(tempfilename,GENERIC_WRITE,FILE_SHARE_READ,
    NULL,CREATE_ALWAYS,FILE_FLAG_SEQUENTIAL_SCAN,NULL); //&sa

  if (FileHandle!=INVALID_HANDLE_VALUE)
  {/*
    STARTUPINFO si;
    memset(&si,0,sizeof(si));
    si.cb=sizeof(si);
    si.dwFlags=STARTF_USESTDHANDLES;
    si.hStdInput=GetStdHandle(STD_INPUT_HANDLE);
    si.hStdOutput=FileHandle;
    si.hStdError=FileHandle;

    PROCESS_INFORMATION pi;
    memset(&pi,0,sizeof(pi));
	*/
    StrBuf workDir(1); //make empty string just in case

    if (tempDir)
    {
      workDir.Reset(NT_MAX_PATH);  //BUGBUG
      ExpandEnvStrs(tempDir,workDir);
    }
    else
    {
#ifdef UNICODE
      DWORD Size=FSF.GetCurrentDirectory(0,NULL);
      if (Size)
      {
        workDir.Reset(Size);
        FSF.GetCurrentDirectory(Size,workDir);
      }
#else
      workDir.Reset(MAX_PATH);
      GetCurrentDirectory(workDir.Size(),workDir);
#endif
    }

    TCHAR consoleTitle[255];
    DWORD tlen = GetConsoleTitle(consoleTitle, ARRAYSIZE(consoleTitle));
    SetConsoleTitle(argv);
	fprintf(stderr, "TODO: CreateProcess %ls\n", fullcmd.Ptr());
/*
    BOOL Created=CreateProcess(NULL,fullcmd,NULL,NULL,TRUE,0,NULL,workDir,&si,&pi);

    if (Created)
    {
      WaitForSingleObject(pi.hProcess,INFINITE);
      CloseHandle(pi.hThread);
      CloseHandle(pi.hProcess);
      allOK=TRUE;
    }
*/
    CloseHandle(FileHandle);

    if (tlen)
      SetConsoleTitle(consoleTitle);
  }

  HANDLE hPlugin = INVALID_HANDLE_VALUE;

  if (allOK)
  {
    if (Opt.MenuForFilelist)
    {
      ShowMenuFromList(tempfilename);
    }
    else
    {
      hPlugin=new TmpPanel();
      if (hPlugin==NULL)
        return INVALID_HANDLE_VALUE;
      ProcessList(hPlugin, tempfilename, Opt.Mode WITH_ANSI_ARG);
    }
  }

  DeleteFile(tempfilename);
  return hPlugin;
}

void ReadFileLines(int fd, DWORD FileSizeLow, TCHAR **argv, TCHAR *args,
                   UINT *numargs, UINT *numchars WITH_ANSI_PARAM)
{
  *numchars = 0;
  *numargs = 0;

  char *FileData = (char *)mmap( NULL, FileSizeLow, PROT_READ, MAP_PRIVATE, fd, 0);
  
  if (FileData==(char *)MAP_FAILED)
    return;

  StrBuf TMP(NT_MAX_PATH); //BUGBUG
  DWORD Len,Pos=0,Size=FileSizeLow;

#ifdef UNICODE
  bool  ucs2 = false;
  if (Size >= 2) switch(*(LPWORD)FileData) {
    case (BOM_UTF8 & 0xFFFF):
      if (Size >= 3 && ((LPBYTE)FileData)[2] == (BYTE)(BOM_UTF8>>16)) {
    case BOM_UCS2_BE:
        goto done;
      }
    default:
      break;
    case BOM_UCS2:
      ucs2 = true;
      Pos += 2;
      break;
  }
#endif

  while (Pos<Size)
  {
#ifdef UNICODE
    if (!ucs2) {
#endif
      char c;
      while (Pos<Size && ((c = FileData[Pos]) == '\r' || c == '\n'))
        Pos++;
      DWORD Off = Pos;
      while (Pos<Size && (c = FileData[Pos]) != '\r' && c != '\n')
        Pos++;
      Len = Pos - Off;
#ifndef UNICODE
      if (Len >= (DWORD)TMP.Size())
        Len = TMP.Size()-1;
      memcpy(TMP.Ptr(), &FileData[Off], Len);
#else // UNICODE
      Len = MultiByteToWideChar(CP_OEMCP, MB_PRECOMPOSED|MB_ERR_INVALID_CHARS,
                                &FileData[Off], Len, TMP, TMP.Size()-1);
    } else {
      wchar_t c;
      --Size;
      while (Pos<Size && ((c = *(wchar_t*)&FileData[Pos]) == L'\r' || c == L'\n'))
        Pos += sizeof(wchar_t);
      DWORD Off = Pos;
      while (Pos<Size && (c = *(wchar_t*)&FileData[Pos]) != L'\r' && c != L'\n')
        Pos += sizeof(wchar_t);
      if (Pos < Size)
        ++Size;
      Len = (Pos-Off)/sizeof(wchar_t);
      if (Len >= (DWORD)TMP.Size())
        Len = TMP.Size()-1;
      memcpy(TMP.Ptr(), &FileData[Off], Len*sizeof(wchar_t));
    }
#endif
    if (!Len)
      continue;

    TMP.Ptr()[Len]=0;

    FSF.Trim(TMP);
    FSF.Unquote(TMP);

#ifndef UNICODE
    if(ANSI)
      CharToOem(TMP,TMP);
#endif

    Len = lstrlen(TMP);
    if (!Len)
      continue;

    if (argv)
      *argv++ = args;

    if (args)
    {
      lstrcpy(args,TMP);
      args+=Len+1;
    }

    (*numchars)+=Len+1;
    ++*numargs;
  }
#ifdef UNICODE
done:
#endif
  munmap((LPVOID)FileData, FileSizeLow);
}

static void ReadFileList (TCHAR *filename, int *argc, TCHAR ***argv WITH_ANSI_PARAM)
{
  *argc = 0;
  *argv = NULL;

#ifdef UNICODE
  StrBuf FullPath;
  GetFullPath(filename, FullPath);
  StrBuf NtPath;
  FormNtPath(FullPath, NtPath);
#else
  const char* NtPath = filename;
#endif

  HANDLE hFile=CreateFile(NtPath,GENERIC_READ,FILE_SHARE_READ,NULL,
    OPEN_EXISTING,0,NULL);

  if(hFile != INVALID_HANDLE_VALUE)
  {
	  DWORD FileSizeLow = GetFileSize(hFile, NULL);
	int fd = GetFileDescriptor(hFile);
    if(fd!=-1 && FileSizeLow!=INVALID_FILE_SIZE)
    {
      UINT i;
      ReadFileLines(fd, FileSizeLow, NULL, NULL, (UINT*)argc, &i WITH_ANSI_ARG);
      *argv = (TCHAR**)malloc(*argc*sizeof(TCHAR*) + i*sizeof(TCHAR));
      ReadFileLines(fd, FileSizeLow, *argv, (TCHAR*)&(*argv)[*argc],
                    (UINT*)argc, &i WITH_ANSI_ARG);
    }
    CloseHandle(hFile);
  }
}

static void ProcessList(HANDLE hPlugin, TCHAR *Name, int Mode WITH_ANSI_PARAM)
{
  if(Mode)
  {
    FreePanelItems(CommonPanels[CurrentCommonPanel].Items,
                   CommonPanels[CurrentCommonPanel].ItemsNumber);
    CommonPanels[CurrentCommonPanel].Items=(PluginPanelItem*)calloc(1,sizeof(PluginPanelItem));
    CommonPanels[CurrentCommonPanel].ItemsNumber=0;
  }
  TmpPanel *Panel=(TmpPanel*)hPlugin;

  int argc;
  TCHAR **argv;
  ReadFileList (Name, &argc, &argv WITH_ANSI_ARG);

  HANDLE hScreen = Panel->BeginPutFiles();

  for(UINT i=0;(int)i<argc;i++)
    Panel->PutOneFile(argv[i]);

  Panel->CommitPutFiles (hScreen, TRUE);
  if (argv)
    free(argv);
}


void ShowMenuFromList(TCHAR *Name)
{
  int argc;
  TCHAR **argv = 0;
#ifndef UNICODE
  static const int ANSI = FALSE;
#endif
  ReadFileList (Name, &argc, &argv WITH_ANSI_ARG);

  FarMenuItem *fmi=(FarMenuItem*)malloc(argc*sizeof(FarMenuItem));
  if (fmi)
  {
    StrBuf TMP(NT_MAX_PATH); //BUGBUG
    for (int i=0;i<argc;++i)
    {
      TCHAR *param,*p=TMP;
      ExpandEnvStrs(argv[i],TMP);
      param=ParseParam(p);
      FSF.TruncStr(param?param:p,67);

#ifndef UNICODE
      strncpy(fmi[i].Text,param?param:p,sizeof(fmi[i].Text)-1);
#else
      fmi[i].Text = wcsdup(param?param:p);
#endif

      fmi[i].Separator=!lstrcmp(param,_T("-"));
			fmi[i].Selected = FALSE;
			fmi[i].Checked = FALSE;
    }
//    fmi[0].Selected=TRUE;

    TCHAR Title[128]; //BUGBUG
    FSF.ProcessName(FSF.PointToName(Name),lstrcpy(Title,_T("*.")),
#ifdef UNICODE
                    ARRAYSIZE(Title),
#endif
                    PN_GENERATENAME);
    FSF.TruncPathStr(Title,64);

    int BreakCode;
    static const int BreakKeys[2]={MAKELONG(VK_RETURN,PKF_SHIFT),0};

    int ExitCode=Info.Menu(Info.ModuleNumber, -1, -1, 0,
      FMENU_WRAPMODE, Title, NULL, _T("Contents"),
      &BreakKeys[0], &BreakCode, fmi, argc);

#ifdef UNICODE
    for (int i=0;i<argc;++i)
      if (fmi[i].Text)
        free((void*)fmi[i].Text);
#endif

    free(fmi);

    if ((unsigned)ExitCode<(unsigned)argc)
    {
      ExpandEnvStrs(argv[ExitCode],TMP);
      TCHAR *p=TMP;
      ParseParam(p);

      FAR_FIND_DATA FindData;
      int bShellExecute=BreakCode!=-1;

      if (!bShellExecute)
      {
        if (TmpPanel::GetFileInfoAndValidate(p,&FindData,FALSE))
        {
          if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
          {
#ifndef UNICODE
            Info.Control(INVALID_HANDLE_VALUE,FCTL_SETPANELDIR,p);
#else
            Info.Control(INVALID_HANDLE_VALUE,FCTL_SETPANELDIR,0,(LONG_PTR)p);
#endif
          }
          else
          {
            bShellExecute=TRUE;
          }
        }
        else
        {
#ifndef UNICODE
          Info.Control(INVALID_HANDLE_VALUE,FCTL_SETCMDLINE,p);
#else
          Info.Control(PANEL_ACTIVE,FCTL_SETCMDLINE,0,(LONG_PTR)p);
#endif
        }
      }

      if (bShellExecute) 
        fprintf(stderr, "TODO: ShellExecute: %ls\n", p);// ShellExecute(NULL,_T("open"),p,NULL,NULL,SW_SHOW);
    }
  }

  if (argv)
    free(argv);
}


#ifndef UNICODE
#define _CONST
#define _OPARG
#else
#define _CONST const
#define _OPARG ,int
#endif
SHAREDSYMBOL HANDLE WINAPI EXP_NAME(OpenFilePlugin)(_CONST TCHAR *Name,const unsigned char *,int DataSize,int OpMode)
#undef _OPARG
#undef _CONST
{
#ifndef UNICODE
#define PNAME_ARG Name
#define pName     Name
#else
  StrBuf pName(NT_MAX_PATH); //BUGBUG
  lstrcpy(pName, Name);
#define PNAME_ARG pName, pName.Size()
#endif

  if (!DataSize || !FSF.ProcessName(Opt.Mask,PNAME_ARG,PN_CMPNAMELIST))
    return INVALID_HANDLE_VALUE;
#undef PNAME_ARG

  if (!Opt.MenuForFilelist)
  {
    HANDLE hPlugin=new TmpPanel();
    if (hPlugin == NULL)
      return INVALID_HANDLE_VALUE;
#ifndef UNICODE
    static const int ANSI = FALSE;
#endif
    ProcessList(hPlugin, pName, Opt.Mode WITH_ANSI_ARG);
    return hPlugin;
  }
  else
  {
    ShowMenuFromList(pName);
    return INVALID_HANDLE_VALUE;
  }
#undef pName
}

SHAREDSYMBOL void WINAPI EXP_NAME(ClosePlugin)(HANDLE hPlugin)
{
  delete (TmpPanel *)hPlugin;
}


SHAREDSYMBOL void WINAPI EXP_NAME(ExitFAR)()
{
  free(PluginRootKey);
  for (int i=0;i<COMMONPANELSNUMBER;++i)
    FreePanelItems(CommonPanels[i].Items, CommonPanels[i].ItemsNumber);
}


SHAREDSYMBOL int WINAPI EXP_NAME(GetFindData)(HANDLE hPlugin,struct PluginPanelItem **pPanelItem,int *pItemsNumber,int OpMode)
{
  TmpPanel *Panel=(TmpPanel *)hPlugin;
  return Panel->GetFindData(pPanelItem,pItemsNumber,OpMode);
}


SHAREDSYMBOL void WINAPI EXP_NAME(GetPluginInfo)(struct PluginInfo *Info)
{
  Info->StructSize=sizeof(*Info);
  Info->Flags=0;
  static const TCHAR *DiskMenuStrings[1];
  DiskMenuStrings[0]=GetMsg(MDiskMenuString);
  Info->DiskMenuStrings=DiskMenuStrings;
#ifndef UNICODE
  static int DiskMenuNumbers[1];
  DiskMenuNumbers[0]=FSF.atoi(Opt.DisksMenuDigit);
  Info->DiskMenuNumbers=DiskMenuNumbers;
#endif
  Info->DiskMenuStringsNumber=Opt.AddToDisksMenu?ARRAYSIZE(DiskMenuStrings):0;
  static const TCHAR *PluginMenuStrings[1];
  PluginMenuStrings[0]=GetMsg(MTempPanel);
  Info->PluginMenuStrings=Opt.AddToPluginsMenu?PluginMenuStrings:NULL;
  Info->PluginMenuStringsNumber=Opt.AddToPluginsMenu?ARRAYSIZE(PluginMenuStrings):0;
  static const TCHAR *PluginCfgStrings[1];
  PluginCfgStrings[0]=GetMsg(MTempPanel);
  Info->PluginConfigStrings=PluginCfgStrings;
  Info->PluginConfigStringsNumber=ARRAYSIZE(PluginCfgStrings);
  Info->CommandPrefix=Opt.Prefix;
}


SHAREDSYMBOL void WINAPI EXP_NAME(GetOpenPluginInfo)(HANDLE hPlugin,struct OpenPluginInfo *Info)
{
  TmpPanel *Panel=(TmpPanel *)hPlugin;
  Panel->GetOpenPluginInfo(Info);
}


SHAREDSYMBOL int WINAPI EXP_NAME(SetDirectory)(HANDLE hPlugin,const TCHAR *Dir,int OpMode)
{
  TmpPanel *Panel=(TmpPanel *)hPlugin;
  return(Panel->SetDirectory(Dir,OpMode));
}


SHAREDSYMBOL int WINAPI EXP_NAME(PutFiles)(HANDLE hPlugin,struct PluginPanelItem *PanelItem,
                              int ItemsNumber,int Move,
#ifdef UNICODE
                              const wchar_t* SrcPath,
#endif
                              int OpMode)
{
#ifndef UNICODE
  StrBuf SrcPath(MAX_PATH);
  GetCurrentDirectory(SrcPath.Size(), SrcPath);
#endif
  TmpPanel *Panel=(TmpPanel *)hPlugin;
  return Panel->PutFiles(PanelItem,ItemsNumber,Move,SrcPath,OpMode);
}


SHAREDSYMBOL int WINAPI EXP_NAME(SetFindList)(HANDLE hPlugin,const struct PluginPanelItem *PanelItem,int ItemsNumber)
{
  TmpPanel *Panel=(TmpPanel *)hPlugin;
  return(Panel->SetFindList(PanelItem,ItemsNumber));
}


SHAREDSYMBOL int WINAPI EXP_NAME(ProcessEvent)(HANDLE hPlugin,int Event,void *Param)
{
  TmpPanel *Panel=(TmpPanel *)hPlugin;
  return(Panel->ProcessEvent(Event,Param));
}


SHAREDSYMBOL int WINAPI EXP_NAME(ProcessKey)(HANDLE hPlugin,int Key,unsigned int ControlState)
{
  TmpPanel *Panel=(TmpPanel *)hPlugin;
  return(Panel->ProcessKey(Key,ControlState));
}


SHAREDSYMBOL int WINAPI EXP_NAME(Configure)(int ItemNumber)
{
  return (!ItemNumber?Config():FALSE);
}

SHAREDSYMBOL int WINAPI EXP_NAME(GetMinFarVersion)()
{
  return FARMANAGERVERSION;
}
