
/*

  JoinConference
  ==============

    Join's a conference

  Version
  =======

    2.0

  Options
  =======

    N_ND->ActiveDoor->SystemOptions
    -------------------------------

    FORWARD

    BACKWARD

    REJOIN

    SYSTEM


    Command Line Arguments
    ----------------------

    2 <ConfNumber>

  Returns
  =======

    OK     - if a new conference is joined
    FAILED - if no conference is joined

*/


#include <exec/types.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <clib/exec_protos.h>
#include <clib/alib_protos.h>

#include <HBBS/ANSI_Codes.h>
#include <HBBS/Defines.h>
#include <HBBS/types.h>
#include <HBBS/structures.h>

#include <HBBS/Hbbscommon_protos.h>
#ifdef __SASC
#include <HBBS/hbbscommon_pragmas_sas.h>
#else
#include <HBBS/hbbscommon_pragmas_stc.h>
#endif

#include <HBBS/Hbbsnode_protos.h>
#ifdef __SASC
#include <HBBS/Hbbsnode_pragmas_sas.h>
#else
#include <HBBS/Hbbsnode_pragmas_stc.h>
#endif

#include <HBBS/release.h>
char *versionstr="$VER: JoinConference "RELEASE_STR;


#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>
#include <time.h>

long __stack=16*1024; // increse this in 4k incrments if you suffer from
                      // random/suprious crashings after or during the running
                      // of your door.

#ifdef __SASC
int CXBRK(void) { return(0); }
int _CXBRK(void) { return(0); }
void chkabort(void) {}
#endif

struct Library *HBBSCommonBase=NULL;
struct Library *HBBSNodeBase=NULL;

struct BBSGlobalData *BBSGlobal=NULL;
struct NodeData *N_ND=NULL;
int N_NodeNum=-1;
char outstr[BIG_STR];


// *********************  global variables..

// 0,0 is lop left for all coords..

UBYTE scra_top=10,scra_left=24,scra_width=32,scra_height=7,scra_highlight=4;
int highlighted_conf=1;
int display_top=1;
int last_display_top;

struct CData
{
  struct List *Confs;
  V_BIGNUM Seen;
  V_BIGNUM Allowed;
};

struct CNode
{
  struct Node node;
  V_BOOL See;
  V_BIGNUM DisplayNumber;
  V_BIGNUM ActualNumber;
  V_BOOL Allowed;
};

struct CData CD;

struct CNode *cn=NULL;
struct ConfData *cd;
struct BoolNode *boolnode;
int loop;

UBYTE str_CSI_CURSORUP[]={'A',0x00};
UBYTE str_CSI_CURSORDOWN[]={'B',0x00};

static VOID cleanup(ULONG num)
{
  if (HBBSNodeBase)
  {
    HBBS_CleanUpDoor();
    CloseLibrary (HBBSNodeBase);
  }

  if (HBBSCommonBase)
  {
    HBBS_CleanUpCommon();
    CloseLibrary (HBBSCommonBase);
  }

  if (num) printf("Door Error = %d\n",num);

  exit(0);
}

static VOID init(VOID)
{
  if(!(HBBSCommonBase = OpenLibrary("HBBSCommon.library",0)))
  {
    cleanup(1);
  }

  if (!(HBBS_InitCommon()))
  {
    cleanup(2);
  }

  if(!(HBBSNodeBase = OpenLibrary("HBBSNode.library",0)))
  {
    cleanup(3);
  }

  if (!(HBBS_InitDoor(N_NodeNum,"JoinConference")))
  {
    cleanup(4);
  }
}

int ConvertDisplayToActual(int displaynum)
{
  int actualnum = -1;
  struct CNode *cn;

  for (cn=(struct CNode *)CD.Confs->lh_Head; cn->node.ln_Succ && actualnum == -1 ;cn=(struct CNode *)cn->node.ln_Succ)
  {
    if (cn->DisplayNumber == displaynum)
    {
      actualnum = cn->ActualNumber;
    }
  }
  return(actualnum);
}

int ConvertActualToDisplay(int actualnum)
{
  int displaynum = -1;
  struct CNode *cn;

  for (cn=(struct CNode *)CD.Confs->lh_Head; cn->node.ln_Succ && displaynum == -1 ;cn=(struct CNode *)cn->node.ln_Succ)
  {
    if (cn->ActualNumber == actualnum)
    {
      displaynum = cn->DisplayNumber;
    }
  }
  return(displaynum);
}

#define DIR_BACKWARD 1
#define DIR_FORWARD 2

int FindConference( int confnum, short Direction )
{
  // this routine selects a conference that the user has access to
  // if the user does not have access to the one passed as the parameter

  // returns 0 if no conference can be found.

  // direction, 1 for backwards, 2 for forwards.

  int newconf=0,loop;
  BOOL CheckedAll=FALSE;

  if (HBBS_AllowConfAccess(confnum,NULL))  // access allowed to conf ?
  {
    return(confnum);
  }
  else // nope, so find a different one..
  {
    loop=confnum;

    while (newconf==0 && !CheckedAll)
    {
      switch (Direction)
      {
        case DIR_FORWARD:
          loop++;
          if (loop>BBSGlobal->Conferences)
          {
            loop=1;
          }
          break;

        case DIR_BACKWARD:
          if (loop>1)
          {
            loop--;
          }
          else
          {
            loop=BBSGlobal->Conferences;
          }
          break;
      }

      if (loop==confnum)
      {
        CheckedAll=TRUE;
      }


      if (HBBS_AllowConfAccess(loop,NULL))
      {
        newconf=loop;
      }
    }
    return(newconf);
  }
}

void HBBS_JoinConference(int newconf,int oldconf) // *C* move to HBBSNode.library...
{
  char outstr[BIG_STR];

  // display "ConfLeft" screen (unless they were not in to start with)

  if (oldconf!=0) DOOR_DisplaySpecialScreen("ConfLeft");
  N_ND->CurrentConf=(struct ConfData *)GetNode(BBSGlobal->ConfList,newconf-1);
  N_ND->User->NormalData->LastConf=newconf;
  N_ND->User->CallData->LastConf=newconf;
  N_ND->MaxDIZLines=N_ND->CurrentConf->MaxDIZLines; //copy this accross
  HBBS_SetAccess();
  HBBS_SetBBSCols();
  HBBS_SetBBSStrings();
  strcpy(outstr,"Joined Conference: ");
  strcat(outstr,N_ND->CurrentConf->node.ln_Name);
  HBBS_AddToCallersLog(outstr);
  if (N_ND->OnlineStatus==OS_ONLINE) DOOR_DisplaySpecialScreen("ConfLogo");
  if (N_ND->OnlineStatus==OS_ONLINE) DOOR_DisplaySpecialScreen("ConfBulletin");
  if (N_ND->OnlineStatus==OS_ONLINE) DOOR_DisplaySpecialScreen("ConfJoined");
}


void strofchr(char *buffer,char fillchr, int len)
{
  int loop;

  for (loop=0;loop<len;loop++)
  {
    buffer[loop]=fillchr;
  }
  buffer[len]=0; // to null terminate!
}

void DisplayConfList( void )
{
  struct CNode *cn;
  short linesout=0;
  short loop;
  short firstnum=1;
  char tmpstr[81];

  if (display_top != last_display_top)
  {
    strofchr(tmpstr,' ',scra_width);

    for (loop=display_top;loop<=0;loop++)
    {
      sprintf(outstr,"\033[%d;%dH" ANSI_RESET,scra_top+linesout,scra_left);
      strcat(outstr,tmpstr);
      DOOR_WriteText(outstr);

      linesout++;
    }

    if (display_top>0) firstnum=display_top;

    for (cn=(struct CNode *)CD.Confs->lh_Head; cn->node.ln_Succ;cn=(struct CNode *)cn->node.ln_Succ)
    {
      if (cn->See)
      {
        if (cn->DisplayNumber>=firstnum && linesout<scra_height) // skip conferences that appear before the top and bottom of the display
        {
          linesout++;

          sprintf(outstr,"\033[%d;%dH" ANSI_RESET,scra_top+linesout-1,scra_left);
          if (linesout == scra_highlight)
          {
            strcat(outstr,ANSI_BG_BLUE);
          }
          sprintf(outstr+strlen(outstr),ANSI_FG_PURPLE"["ANSI_FG_WHITE"%02d"ANSI_FG_PURPLE"]" ANSI_FG_CYAN" - "ANSI_FG_WHITE"%s",cn->DisplayNumber,cn->node.ln_Name);

          strofchr(outstr+strlen(outstr),' ',scra_width-strlen(cn->node.ln_Name)-7);

          DOOR_WriteText(outstr);

        }

      }
    }

    if (linesout<scra_height)
    {
      // we have some lines to blank out

      while (linesout<scra_height)
      {
        sprintf(outstr,"\033[%d;%dH" ANSI_RESET,scra_top+linesout++,scra_left);
      strcat(outstr,tmpstr);
        DOOR_WriteText(outstr);
      }

    }
    last_display_top=display_top;

    sprintf(outstr,"\033[%d;%dH" ANSI_RESET,scra_top+scra_highlight-1,scra_left+5);
    DOOR_WriteText(outstr);

  }

}

int SelectConference(int oldconf)
{
  int newconf=0;
  BOOL done,moveit;

  DOOR_DisplaySpecialScreen("Conf_TOP");

  highlighted_conf=oldconf;
  display_top=ConvertActualToDisplay(oldconf)+1-scra_highlight;
  last_display_top=display_top+1; // just so that it's not the same.

    if (HBBS_NodesInList(CD.Confs)==BBSGlobal->Conferences) // check we got a list containing all the confs
    {
      // OK, list is now created so we wanna display them to the user..

      if (CD.Seen==0)
      {
        DOOR_WriteText("You have no conferences to choose from!\r\n");
        DOOR_DisplaySpecialScreen("Conf_BOT");
      }
      else
      {
        DisplayConfList();

        done=FALSE;
        do
        {
          moveit=FALSE;
				  if (DOOR_CheckRaw(CR_NONE))
				  {

/* enable these lines to get each keypress dumped to the screen..

				    if (N_ND->CurrentLine[0]>27)
				    {
              sprintf(outstr,"%c (%d), ",N_ND->CurrentLine[0],(UBYTE)N_ND->CurrentLine[0]);
              DOOR_WriteText(outstr);
				    }
				    else
				    {
              sprintf(outstr,"!! (%d), ",(UBYTE)N_ND->CurrentLine[0]);
              DOOR_WriteText(outstr);
				    }
*/

				    // N_ND->CurrentLine[2] will be set to either GET_SERIAL or GET_CONSOLE depending on where
				    // the input came from, we need to pass this to CheckCSI so it gets the rest of the keypress
				    // from the same place!

            switch (DOOR_CheckCSI(N_ND->CurrentLine[0],N_ND->CurrentLine[2]))
            {
              case KEY_CSI:
                if (strcmp(N_ND->csistring,str_CSI_CURSORUP) == 0)
                {
                  display_top --;
                  moveit=TRUE;
                }
                else
                if (strcmp(N_ND->csistring,str_CSI_CURSORDOWN) == 0)
                {

                  display_top++;
                  moveit=TRUE;
                }
                break;
              case KEY_NONE:
                switch(N_ND->CurrentLine[0])
                {
                  case '\r':
                    newconf = ConvertDisplayToActual(display_top - 1 + scra_highlight);
                    done=TRUE;
                    break;
                  case 'Q':
                  case 'q':
                    done=TRUE;
                    newconf =-1;
                }
                break;
            }
            if (moveit)
            {
              // check ranges

              if (display_top < (0-scra_highlight)+2)
                display_top = (0-scra_highlight)+2;
              if (display_top > ((int)CD.Seen-(int)scra_highlight)+1)
                display_top = ((int)CD.Seen-(int)scra_highlight)+1;

              // and display list again!

              DisplayConfList();
            }
				  }




				} while (!done);

        sprintf(outstr,"\033[%d;%dH" ANSI_RESET,scra_top+scra_height,0);
        DOOR_WriteText(outstr);

      }
    }
    else DOOR_WriteText("Out Of Memory!\r\n");



  return(newconf);
}

V_BOOL JoinIt(int confnum, int lastconf)
{
  if (confnum>0 && confnum<=BBSGlobal->Conferences)
  {
    if (HBBS_AllowConfAccess(confnum,NULL))
    {
      HBBS_JoinConference(confnum,lastconf);
      return(TRUE);
    }
  }
  return(FALSE);
}


void DoorMain(int argc,char *argv[])
{
  int confnum=0;
  int lastconf=0;
  V_BOOL JoinedConf=FALSE;

  // used to create the list.
  V_BIGNUM lastdisplaynumber=1;
  short loop;
  struct CNode *cn;


  // first we create a list containing the numbers and names of the conferences
  // that the user is allowed to join.
  CD.Seen=0;
  CD.Allowed=0;

  if (CD.Confs=HBBS_CreateList())
  {
    for (loop=0;loop<BBSGlobal->Conferences;loop++) // loop starts at 0!
    {
      cd=(struct ConfData *)GetNode(BBSGlobal->ConfList,loop);
      boolnode=(struct BoolNode *)GetNode(N_ND->User->ConfAcs.See,loop);

      if (cn = (struct CNode *)AllocVec(sizeof(struct CNode),MEMF_PUBLIC|MEMF_CLEAR))
      {
        strNcpy(outstr,cd->node.ln_Name,scra_width-7); // width of scroll area - a bit for the number and other bitsnbobs.
        if (cn->node.ln_Name = DupStr(outstr))
        {
          if (cn->Allowed=HBBS_AllowConfAccess(loop+1,NULL)) CD.Allowed++;
          if (cn->See=boolnode->Boolean)
          {
            CD.Seen++;
            cn->DisplayNumber=lastdisplaynumber++;
          }

          else cn->DisplayNumber=0; // not diplayed ever!

          cn->ActualNumber = loop+1;

          AddTail(CD.Confs,(struct Node *)cn);
        }
      }
    }

    if (N_ND->CurrentConf) lastconf=N_ND->CurrentConf->ConfNum;

    if (N_ND->ActiveDoor->SystemOptions && N_ND->ActiveDoor->SystemOptions[0]) // check system options first..
    {
      if (stricmp("REJOIN",N_ND->ActiveDoor->SystemOptions)==0)
      {
        JoinedConf=JoinIt(lastconf,lastconf);
      }
      else
      {
        if (stricmp("BACKWARD",N_ND->ActiveDoor->SystemOptions)==0)
        {
          confnum=FindConference(lastconf-1,DIR_BACKWARD);
        }
        else
        if (stricmp("FORWARD",N_ND->ActiveDoor->SystemOptions)==0)
        {
          confnum=FindConference(lastconf+1,DIR_FORWARD);
        }
        else
        if (stricmp("SYSTEM",N_ND->ActiveDoor->SystemOptions)==0)
        {
          if (N_ND->User->CallData->PreferredConf)
          {
            confnum=FindConference(N_ND->User->CallData->PreferredConf,DIR_FORWARD);
          }
          else
          {
            confnum=FindConference(N_ND->User->CallData->LastConf,DIR_FORWARD);
          }
        }
        else
        {
          DOOR_SysopText("Invalid System Option Specified");
        }

        if (confnum!=lastconf) JoinedConf=JoinIt(confnum,lastconf);
      }
    }
    else // then check command line options!
    {
      if (argc==3) // have we got any to check ?
      {
        if (sscanf(argv[2],"%ld",&confnum))
        {
          confnum = ConvertDisplayToActual(confnum);

          if (confnum!=-1 && confnum!=lastconf) JoinedConf=JoinIt(confnum,lastconf);
        }
      }
      else // nope, so display a menu
      {
        confnum=SelectConference(lastconf);
        if (confnum!=lastconf && confnum!=-1) JoinedConf=JoinIt(confnum,lastconf);
      }
      if (!JoinedConf && confnum!=-1)
      {
        if (confnum==lastconf)
        { 
          if (!DOOR_DisplaySpecialScreen("Conf_Cancel"))
            DOOR_WriteText(ANSI_RESET ANSI_FG_RED ANSI_BOLD "You are already in that conference, use RJ to rejoin!\r\n" ANSI_RESET);
        }
        else
        { 
          if (!DOOR_DisplaySpecialScreen("Conf_Cancel"))
          DOOR_WriteText(ANSI_RESET ANSI_FG_RED ANSI_BOLD "Invalid Conference Number/Access Denied!\r\n" ANSI_RESET);
        }
      }
    }

    if (JoinedConf)
    {
      DOOR_Return("OK");
    }
    else
    {
      DOOR_Return("FAILED");
      DOOR_Continue(FALSE);
    }
    FreeStrList(CD.Confs);
  }

}

int main(int argc,char *argv[])
{
  if (sscanf(argv[1],"%d",&N_NodeNum)==0)
  {
    printf("Invalid/No Paramaters for door!\n");
    exit (20);
  }
  init();

  if (BBSGlobal=HBBS_GimmeBBS())
  {
    if (N_ND=HBBS_NodeDataPtr(N_NodeNum)) // this should not fail in normal circumstances..
    {
      DoorMain(argc,argv);
    }
  }
  cleanup(0);
}
