/* Associate (C) 1995 Dominic Clifton - AKA Hydra/LSD */

// note: you MUST have a global define KS20 or KS30 set or CRASH!!!
// I use SCOptions to set it...


#define MAIN
#include "includes.h"
static UBYTE   *VersTag = "$VER: Associate 1.6 (16.07.1997)";
#include "vars.h"
#include <wb2cli.h>

char *iconname="Progdir:Associate";
char msgstr[300];
BOOL done=FALSE;
char cfgname[]="S:Associate.cfg";
BOOL All,ttall,AllThis;
LONG ttallval;
ULONG doalldrawers=0;
struct TypeNode *MatchType;
LONG IconsToProcess;

/*
   SHITTTTTTTTTTTTTTTTTTTTTT Nodes in a list that point to lists that point to
   more nodes are VERY confusing! :-)
*/

// next four functions nicked from YAK source code..  ta very much..
// (but they are modified a bit tho..)

char __regargs *
TTString(struct DiskObject *diskobj,char *name, char *def)
{
  char *what;
  if (diskobj)
    if (what = FindToolType(diskobj->do_ToolTypes, name))
      return what;
  return def;
}

/* like ArgInt() */
LONG __regargs
TTInt(struct DiskObject *diskobj,char *name, LONG def)
{
  char *what;
  if (diskobj)
    if (what = FindToolType(diskobj->do_ToolTypes, name))
      StrToLong(what, &def);
  return def;
}

struct Node *GetNode(struct List *lh, UWORD n)
{
  struct Node *ln;

  for (ln = lh->lh_Head; n--; ln = ln->ln_Succ)
    ;
  return ln;
}

UWORD GetNodeNum(struct List *lh, struct Node *node)
{
  struct Node *ln;
  UWORD i;

  for (i = 0, ln = lh->lh_Head; ln != node; ln = ln->ln_Succ, i++)
    ;
  return i;
}

//  ok now for the bitchy list stuff..

void FreeNameList(struct List *FileList)
{
  struct Node *Current,*Next;

  if (Current=FileList->lh_Head)
  {
    while (FileList->lh_Head->ln_Succ)
    {
      Next=Current->ln_Succ;
      if (Current->ln_Name)
      {
        free(Current->ln_Name);
      }
      Remove(Current);
      FreeMem(Current,sizeof(struct Node));
      Current=Next;
    }
  }
}

struct Node *NewNameNode(struct List *NameList,char *str)
{
  struct Node *new=NULL;

  if (new=AllocMem(sizeof(struct Node),MEMF_PUBLIC))
  {
    new->ln_Name=strdup(str);
    AddTail(NameList,new);
  }
  return(new);
}

void FreeNameNode(struct Node *node)
{
  if (node->ln_Name)
  {
    free(node->ln_Name);
  }
}


void FreeTypeNode(struct TypeNode *new)
{
  if (new->nameplist)
  {
    FreeNameList(new->nameplist);
    FreeMem(new->nameplist,sizeof (struct List));
  }
  if (new->fileplist)
  {
    FreeNameList(new->fileplist);
    FreeMem(new->fileplist,sizeof (struct List));
  }
  if (new->IconName) free(new->IconName);
  FreeNameNode(&new->typenode);
}

void FreeTypeList(struct List *TypeList)
{
  struct TypeNode *Current,*Next;

  if (Current=(struct TypeNode*)TypeList->lh_Head)
  {
    while (TypeList->lh_Head->ln_Succ)
    {
      Next=(struct TypeNode *)Current->typenode.ln_Succ;
      FreeTypeNode(Current);
      Remove((struct Node*) Current);
      FreeMem(Current,sizeof(struct TypeNode));
      Current=Next;
    }
  }
}


short AllocTypeNode(struct TypeNode *new)
{
  short retval=FALSE;
  if (new->nameplist=AllocMem(sizeof(struct List),MEMF_ANY))
  {
    NewList(new->nameplist);
    if (new->fileplist=AllocMem(sizeof(struct List),MEMF_ANY))
    {
      NewList(new->fileplist);
      if (new->IconName=malloc(256))
      {
        new->IconName[0]='\0';
        retval=TRUE;
      }
    }
  }
  return (retval);
}

struct TypeNode *NewTypeNode(struct List *TypeList,char *str)
{
  struct TypeNode *new;

  if (new=AllocMem(sizeof(struct TypeNode),MEMF_PUBLIC))
  {

    new->typenode.ln_Name=strdup(str); // I should REALLY error check this line.
    new->nameplist=NULL; // initialize structure..
    new->fileplist=NULL;
    new->IconName=NULL;
    new->RunInfo=0;

    if (AllocTypeNode(new))
    {
      AddTail(TypeList,(struct Node *)new);
    }
    else
    {
      FreeTypeNode(new);
      FreeMem(new,sizeof(struct TypeNode));
      new=NULL;
    }
  }
  return(new); // return NULL for fail or the pointer to the new structure..
}

void SetDefault( void )
{
  struct TypeNode *mytn;
  struct Node *mynode;
  struct List *namelist;

  mytn=NewTypeNode(typelist,"Picture");
  if (mytn)
  {
    mytn->IconName="ENV:Sys/Def_Misc.info";
    namelist=mytn->nameplist;
    mynode=NewNameNode(namelist,"#?.PIC");
    mynode=NewNameNode(namelist,"#?.GIF");
    mynode=NewNameNode(namelist,"#?.JPG");
    mynode=NewNameNode(namelist,"#?.JPEG");
    mynode=NewNameNode(namelist,"#?.IFF");
    mynode=NewNameNode(namelist,"#?.ILBM");
    namelist=mytn->fileplist;
    mynode=NewNameNode(namelist,"FORM????ILBM#?");
    mynode=NewNameNode(namelist,"GIF#?");
  }
  mytn=NewTypeNode(typelist,"Source Code");
  if (mytn)
  {
    mytn->IconName="ENV:Sys/def_Source.info";
    namelist=mytn->nameplist;
    mynode=NewNameNode(namelist,"#?.c");
    mynode=NewNameNode(namelist,"#?.s");
    mynode=NewNameNode(namelist,"#?.i");
    mynode=NewNameNode(namelist,"#?.h");
    mynode=NewNameNode(namelist,"#?.p");
    mynode=NewNameNode(namelist,"#?.pas");
  }
}

short SetUp( void )
{
  short retval=FALSE;

  if (ReqToolsBase = (struct ReqToolsBase *) OpenLibrary (REQTOOLSNAME, REQTOOLSVERSION))
  {
    if (typelist=AllocMem(sizeof(struct List),MEMF_ANY))
    {
      NewList(typelist);
      if(IconBase = OpenLibrary("icon.library",37))
      {
#ifdef KS20
        if (WorkbenchBase=OpenLibrary("workbench.library",37))
#endif
#ifdef KS30
        if (WorkbenchBase=OpenLibrary("workbench.library",39))
#endif
        {
          if (ascport=CreateMsgPort())
          {
            if (filereq = rtAllocRequestA (RT_FILEREQ, NULL))
            {
              retval=TRUE;
            }
            else rtEZRequest("Could not allocate requester",okstr,NULL,(struct TagItem *)&reqtags,NULL);
          }
          else rtEZRequest("Could Not Create Message Port!",okstr,NULL,(struct TagItem *)&reqtags,NULL);
        }
#ifdef KS20
        else rtEZRequest("You Need workbench.library v37+!",okstr,NULL,(struct TagItem *)&reqtags,NULL);
#endif
#ifdef KS30
        else rtEZRequest("You Need workbench.library v39+!",okstr,NULL,(struct TagItem *)&reqtags,NULL);
#endif
      }
      else rtEZRequest("You Need icon.library v37!",okstr,NULL,(struct TagItem *)&reqtags,NULL);
    }
    else rtEZRequest("Unable to alloc memory!",okstr,NULL,(struct TagItem *)&reqtags,NULL);
  }
  return(retval);
}

void KillAppIcon( void )
{
  if (appicon) RemoveAppIcon(appicon);
  if (ascport)
  {
    while(appmsg=(struct AppMessage *)GetMsg(ascport))
        ReplyMsg((struct Message *)appmsg);
  }
}

void CleanUp( void )
{
  KillAppIcon();
  if ( ascport       ) DeleteMsgPort(ascport);
  if ( dobj          ) FreeDiskObject(dobj);
  if ( WorkbenchBase ) CloseLibrary(WorkbenchBase);
  if ( IconBase      ) CloseLibrary(IconBase);
  if ( filereq       ) rtFreeRequest (filereq);
  if ( ReqToolsBase  ) CloseLibrary ((struct Library *)ReqToolsBase);
  if ( typelist      )
  {
    FreeTypeList(typelist);
    FreeMem(typelist,sizeof(struct List));
  }
}

short OpenAppIcon( void )
{
  short retval=FALSE;
  if ((dobj=GetDiskObject(iconname))==NULL)
  {
    sprintf(msgstr,"Could Not Open \"%s.info\" to create the AppIcon\n"
                   "You might still have to rename associate, see the docs!",iconname);
    rtEZRequest(msgstr,okstr,NULL,(struct TagItem *)&reqtags,NULL);
  }
  else
  {
    dobj->do_Type=NULL; // set to null for appicon..,
    // temp fix until loadprefs has been written..
    dobj->do_CurrentX=TTInt(dobj,"ICONX",NO_ICON_POSITION);
    dobj->do_CurrentY=TTInt(dobj,"ICONY",NO_ICON_POSITION);
    // wb don't like only one of the coords set.. :-)
    if (dobj->do_CurrentX >= 0 && dobj->do_CurrentY == NO_ICON_POSITION) dobj->do_CurrentY=1;
    if (dobj->do_CurrentY >= 0 && dobj->do_CurrentX == NO_ICON_POSITION) dobj->do_CurrentX=1;
    if ((appicon=AddAppIconA(0L,0L,"Associate",ascport,NULL,dobj,NULL))==NULL)
    {
      rtEZRequest("Could Not Create App Icon!",okstr,NULL,(struct TagItem *)&reqtags,NULL);
    }
    else
    {
      retval=TRUE;
    }
  }
  return(retval);
}

void DoOptions( void )
{
  if (!SetupScreen())
  {
    if (!OpenAssociateWindow())
    {
  #ifdef KS20
      LastTypeClicked=-1;
  #endif
      GT_SetGadgetAttrs(AssociateGadgets[GD_TypeList], AssociateWnd, NULL,
                        GTLV_Labels, (ULONG)typelist,
                        GTLV_Selected,0,
                        TAG_END);
      UpdateLists();
      do
      {
        Wait (1 << AssociateWnd->UserPort->mp_SigBit);
      } while (HandleAssociateIDCMP());
      CloseAssociateWindow();
    }
    CloseDownScreen();
  }
}

void CreateIcon(char *filename, struct TypeNode *tnode)
{
  char *iconname;
  struct DiskObject *icn,*org=NULL;
  int offset,orgoffset,newoffset;
  char **ttypes=NULL,**oldttypes,*str;
  BOOL cancel=FALSE;
  iconname=strdup(tnode->IconName);
  removeinfo(iconname);
  if (icn=GetDiskObject(iconname))
  {
    icn->do_CurrentX=NO_ICON_POSITION;
    icn->do_CurrentY=NO_ICON_POSITION;

// Added this next bit for v1.3

    if (org=GetDiskObject(filename))
    {
      if (org->do_ToolTypes && org->do_ToolTypes[0])
      {
        offset=0;
        while (icn->do_ToolTypes[offset]) offset++;

        orgoffset=0;
        while (org->do_ToolTypes[orgoffset]) orgoffset++;

        // save old pointer
        oldttypes=icn->do_ToolTypes;

        if (str=AllocVec(strlen(filename)+40,MEMF_CLEAR))
        {
          sprintf(str,"An Icon Already Exists for file\n\"%s\"",filename);

          if (!ttall) ttallval=rtEZRequest(str,"_Add New ToolTypes|_Old ToolTypes Only|_New ToolTypes Only|_Skip",NULL,(struct TagItem *)&reqtags,NULL);
          switch (ttallval)
          {
            case 1:

              if (ttypes=(char **)AllocVec((1+offset+orgoffset)*sizeof(LONG),MEMF_PUBLIC|MEMF_CLEAR))
              {
                newoffset=0;

                offset=0;
                while (icn->do_ToolTypes[offset])
                {
                  ttypes[newoffset]=icn->do_ToolTypes[offset];
                  offset++;
                  newoffset++;
                }
                offset=0;
                while (org->do_ToolTypes[offset])
                {
                  ttypes[newoffset]=org->do_ToolTypes[offset];
                  offset++;
                  newoffset++;
                }
                icn->do_ToolTypes=ttypes;
              }
              break;
            case 2:
              icn->do_ToolTypes=org->do_ToolTypes;
              break;
/*
            case 3:
              break;
*/
            case 0:
              cancel=TRUE;
              break;
          }
          FreeVec(str);
          if ((!cancel) && (IconsToProcess>0) && (!ttall))
          {
            if (rtEZRequest("Repeat for remaining icons ?","_Yes|_No",NULL,(struct TagItem *)&reqtags,NULL))
            {
              ttall=TRUE;
            }
          }
        }
      }


    }

    if (!cancel) PutDiskObject(filename,icn);
    if (ttypes)
    {
      icn->do_ToolTypes=oldttypes; // restore old pointer so AmigaDOS can free it..
    }
    FreeDiskObject(icn);
    if (org)
    {
      FreeDiskObject(org);
      if (ttypes) FreeVec(ttypes); // and we free our own set here..
    }
    if (tnode->RunInfo)
    {
      DoInfo(filename);
    }
    free(iconname);
  }
  else
  {
    rtEZRequest("Error: Can't open \"%s.info\"\nCheck your config",okstr,NULL,(struct TagItem *)&reqtags,iconname);
  }
}


void PickType(char *filename)
{
  if (!SetupScreen())
  {
    if (!OpenPickWindow())
    {
#ifdef KS20
      LastPickClicked=-1;
#endif
      GT_SetGadgetAttrs(PickGadgets[GD_PickType], PickWnd, NULL,
                        GTLV_Labels, (ULONG)typelist,
                        GTLV_Selected,0,
                        TAG_END);
      do
      {
        Wait (1 << PickWnd->UserPort->mp_SigBit);
      } while (HandlePickIDCMP());

      // this is the only place temptnode is relied on between functions..
      // so be carefull

      if (temptnode) // idcmp handling will set this to NULL if pickwindow is cancelled
      {
        CreateIcon(filename,temptnode);
      }
      ClosePickWindow();
    }
    CloseDownScreen();
  }
}

BOOL MatchFile(char *matchpattern, char *filename)
{
  BPTR File;
  char *buffer;
  LONG buflen;
  BOOL retval=FALSE;
  char match[256];
  short loop;

  if (buffer=malloc(21))
  {
    if (File=Open(filename,MODE_OLDFILE))
    {
      if (buflen=Read(File,buffer,20)) // read 1st 20 bytes
      {
        for (loop=0;loop<buflen;loop++) // and check..
        {
          if (buffer[loop]==0) buffer[loop]='.'; // replace null bytes with .'s..
        }
        ParsePatternNoCase(matchpattern,match,255);
        if (MatchPatternNoCase(match,buffer))
        {
          retval=TRUE;
        }
      }
      Close(File);
    }
    free(buffer);
  }
  return(retval);
}

LONG MatchOK(char *filename,struct TypeNode *tnode)
{
  LONG result;

  MatchType=tnode;

  if (!All)
  {
    sprintf(msgstr,"Matched file \"%s\" against\n"
                   "type \"%s\"\n"
                   "Do you want to use this type\n"
                   "or continue searching ?",FilePart(filename),tnode->typenode.ln_Name);
    result=rtEZRequest(msgstr,"OK!|_All First|All _This|Continue..",NULL,(struct TagItem *)&reqtags,NULL);
    if (result==2) All=TRUE;
    if (result==3)
    {
      All=TRUE;
      AllThis=TRUE;
    }
    return(result);
  }
  else return(1);
}

void DoIcon( char *filename)
{
  struct TypeNode *tnode;
  struct Node *node;
  char match[256];
  char temp;
  LONG Matched=FALSE;
  char *iconname;
  struct DiskObject *icn;

  temp=filename[strlen(filename)-1];
  if (temp!='/' && temp!=':')
  {
    if (!AllThis)
    {
      for (tnode = (struct TypeNode *) typelist->lh_Head ; !Matched && tnode->typenode.ln_Succ ; tnode = (struct TypeNode *)tnode->typenode.ln_Succ)
      {
        for (node = tnode->nameplist->lh_Head ; !Matched && node->ln_Succ ; node = node->ln_Succ)
        {
          ParsePatternNoCase(node->ln_Name,match,255);
          if (MatchPatternNoCase(match,FilePart(filename)))
          {
            Matched=MatchOK(filename,tnode);
          }
        }
        if (!Matched)
        {
          for (node = tnode->fileplist->lh_Head ; !Matched && node->ln_Succ ; node = node->ln_Succ)
          {
            if (MatchFile(node->ln_Name,filename))
            {
              Matched=MatchOK(filename,tnode);
            }
          }
        }
      }
    }
    else
    {
      Matched=TRUE;
    }

    if (Matched)
    {
      CreateIcon(filename,MatchType);
    }
    else
    {
      sprintf(msgstr,"Sorry, I could not find a match\n"
                     "for the file \"%s\"",FilePart(filename));
      if (rtEZRequest(msgstr,"Select A _Type|_Skip",NULL,(struct TagItem *)&reqtags,NULL))
      {
        PickType(filename);
      }
    }
  }
  else
  {
    if (iconname=strdup(filename))
    {
      sprintf(msgstr,"Copy Default %s Icon \nto %s ?",temp == '/' ? "Drawer" : "Disk",iconname);
      if (temp=='/')
      {

        if ((doalldrawers==2) || (doalldrawers=rtEZRequest(msgstr,"_Yes|_All|_Cancel",NULL,(struct TagItem *)&reqtags,NULL)))
        {
          iconname[strlen(iconname)-1]=0;
          if (icn=GetDiskObject("env:sys/def_drawer"))
          {
            icn->do_CurrentX=NO_ICON_POSITION;
            icn->do_CurrentY=NO_ICON_POSITION;
            PutDiskObject(iconname,icn);
            FreeDiskObject(icn);
          }
        }
      }
      else // disk..
      {
        if (rtEZRequest(msgstr,"_Yes|_Cancel",NULL,(struct TagItem *)&reqtags,NULL))
        {
          strcat(iconname,"disk");
          if (icn=GetDiskObject("env:sys/def_disk"))
          {
            icn->do_CurrentX=NO_ICON_POSITION;
            icn->do_CurrentY=NO_ICON_POSITION;
            PutDiskObject(iconname,icn);
            FreeDiskObject(icn);
          }
        }
      }
      free(iconname);
    }
  }
}

void CheckMessages( void )
{
  short loop;
  char *filename;

  All=FALSE;
  AllThis=FALSE;
  MatchType=NULL;
  doalldrawers=0;
  ttall=FALSE;
  ttallval=0;
  while(appmsg=(struct AppMessage *)GetMsg(ascport))
  {
    if(appmsg->am_NumArgs==0L)
    {
      DoOptions();
    }
    else
    if(appmsg->am_NumArgs>0L) // could it be less than 0 ?
    {
      IconsToProcess=appmsg->am_NumArgs;
      if (filename=malloc(256))
      {
        for(loop=0;loop<appmsg->am_NumArgs;loop++)
        {
          IconsToProcess--;
          NameFromLock(appmsg->am_ArgList[loop].wa_Lock,filename,255);
          AddPart(filename,appmsg->am_ArgList[loop].wa_Name,255);
          DoIcon(filename);
        }
        free(filename);
      }
    }
    ReplyMsg((struct Message *)appmsg);
  }
}

void stripcr( char *str)
{
  if (str[strlen(str)-1]=='\n') str[strlen(str)-1]=0;
  if (str[strlen(str)-1]=='\r') str[strlen(str)-1]=0;
}

void SavePrefs( void )
{
  FILE *File;
  struct TypeNode *tnode;
  struct Node *node;

  if (File=fopen(cfgname,"w"))
  {
    for (tnode = (struct TypeNode *) typelist->lh_Head ;tnode->typenode.ln_Succ ; tnode = (struct TypeNode *)tnode->typenode.ln_Succ)
    {
      fprintf(File,"|STARTLIST\n"
                   "%s\n" // node name
                   "%s\n" // icon name
                   "%d\n", // RunInfo
                   tnode->typenode.ln_Name,tnode->IconName,tnode->RunInfo);
      if (tnode->nameplist->lh_Head->ln_Succ)
      {
        fprintf(File,"|NAMEPATTERN\n");
        for (node = tnode->nameplist->lh_Head ;node->ln_Succ ; node = node->ln_Succ)
        {
          fprintf(File,"%s\n",node->ln_Name);
        }
      }
      if (tnode->fileplist->lh_Head->ln_Succ)
      {
        fprintf(File,"|FILEPATTERN\n");
        for (node = tnode->fileplist->lh_Head ;node->ln_Succ ; node = node->ln_Succ)
        {
          fprintf(File,"%s\n",node->ln_Name);
        }
      }
      fprintf(File,"|ENDLIST\n");
    }
    fclose(File);
  }
}

short LoadPrefs( void )
{
  FILE *File;
  char str[256];
  char what='x';

  // load prefs.  return 1 if OK 0 otherwise..

  if (File=fopen(cfgname,"r"))
  {
    while (!feof(File))
    {
      fgets(str,255,File);
      if (!feof(File))
      {
        stripcr(str);

        // ok, so i could use a "flag" here :-) buy hey! what do i care..

        if (stricmp(str,"|ENDLIST")==0)
        {
          what='x';
        }
        else
        {
          if (stricmp(str,"|NAMEPATTERN")==0)
          {
            what='n';
          }
          else
          {
            if (stricmp(str,"|FILEPATTERN")==0)
            {
              what='f';
            }
            else
            {
              if (stricmp(str,"|STARTLIST")==0)
              {
                what='l';
              }
              else
              {
                if (what!='x')
                {
                  switch(what)
                  {
                    case 'n':
                      NewNameNode(temptnode->nameplist,str);
                      break;
                    case 'f':
                      NewNameNode(temptnode->fileplist,str);
                      break;
                    case 'r':
                      sscanf(str,"%d",&temptnode->RunInfo);
                      what='x';
                      break;
                    case 'i':
                      strncpy(temptnode->IconName,str,255); // limited..
                      what='r';
                      break;
                    case 'l':
                      temptnode=NewTypeNode(typelist,str);
                      what='i';
                      break;
                  }
                }
              }
            }
          }
        }
      }
    }
    fclose(File);
    return(1);
  }
  return(0);
}

void main(int argc,char *argv[])
{
  WB2CLI((struct WBStartup *)argv,4000,DOSBase);  /* so you get the default path! */
  SetUp();
  if (!LoadPrefs())
  {
    SetDefault();
    DoOptions();
  }

  if (argc==2)
  {
    DoIcon(argv[1]);
  }
  else
  {
    if (OpenAppIcon())
    {
      while (!done)
      {
        WaitPort(ascport);
        CheckMessages();
      }
    }
    else
    {
      rtEZRequest("Could not create an AppIcon!","_Quit!",NULL,(struct TagItem *)&reqtags,NULL);
    }
  }
  CleanUp();
}
