/*
	GIER saveload

	(C) Copyright 2001 by Mogens Kjaer


   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <signal.h>
#include <string.h>

#include "GIER.h"
#include "common.h"

typedef struct
{
  char *name;
  int elementsize;
  int hasbeenread;
  void *ptr;
} Field;

static int Mode_value, Mode_drumsave_value;
static int kb1_visible, kb2_visible, typewriter_visible, reader_visible, punch_visible, printer_visible,
  plotter_visible, nimbi_visible;

static Field fields[]=
{
  {"LI", sizeof(LI), 0, &LI},
  {"OR", sizeof(OR), 0, &OR},
  {"MD", sizeof(MD), 0, &MD},
  {"H", sizeof(H), 0, &H},
  {"MQ", sizeof(MQ), 0, &MQ},
  {"AC", sizeof(AC), 0, &AC},
  {"AD0", sizeof(AD0), 0, &AD0},
  {"AD1", sizeof(AD1), 0, &AD1},
  {"IN", sizeof(IN), 0, &IN},
  {"OT", sizeof(OT), 0, &OT},
  {"AD2", sizeof(AD2), 0, &AD2},
  {"SR", sizeof(SR), 0, &SR},
  {"TK", sizeof(TK), 0, &TK},
  {"TG", sizeof(TG), 0, &TG},
  {"BL", sizeof(BL), 0, &BL},
  {"BY", sizeof(BY), 0, &BY},
  {"BS", sizeof(BS), 0, &BS},
  {"BT", sizeof(BT), 0, &BT},
  {"TD", sizeof(TD), 0, &TD},
  {"TBA", sizeof(TBA), 0, &TBA},
  {"Tromle_size", sizeof(Tromle_size), 0, &Tromle_size},
  {"track0_open", sizeof(track0_open), 0, &track0_open},
  {"track1_31_open", sizeof(track1_31_open), 0, &track1_31_open},
  {"sound_enabled", sizeof(sound_enabled), 0, &sound_enabled},
  {"reader_parity_error", sizeof(reader_parity_error), 0, &reader_parity_error},
  {"kb1_visible", sizeof(kb1_visible), 0, &kb1_visible},
  {"kb2_visible", sizeof(kb2_visible), 0, &kb2_visible},
  {"typewriter_visible", sizeof(typewriter_visible), 0, &typewriter_visible},
  {"reader_visible", sizeof(reader_visible), 0, &reader_visible},
  {"punch_visible", sizeof(punch_visible), 0, &punch_visible},
  {"printer_visible", sizeof(printer_visible), 0, &printer_visible},
  {"plotter_visible", sizeof(plotter_visible), 0, &plotter_visible},
  {"nimbi_visible", sizeof(nimbi_visible), 0, &nimbi_visible},
  {"Mode", sizeof(Mode_value), 0, &Mode_value},
  {"Mode_drumsave", sizeof(Mode_drumsave_value), 0, &Mode_drumsave_value},
  {"ind_ka", sizeof(ind_ka), 0, &ind_ka},
  {"ind_kb", sizeof(ind_kb), 0, &ind_kb},
  {"switch_k0", sizeof(switch_k0), 0, &switch_k0},
  {"debug", sizeof(debug), 0, &debug},
  {"MA", sizeof(MA), 0, &MA},
  {"TO_error", sizeof(TO_error), 0, &TO_error},
  {"TR_error", sizeof(TR_error), 0, &TR_error},
  {"LI_drum", sizeof(LI_drum), 0, &LI_drum},
  {"TA", sizeof(TA), 0, &TA},
  {"drumsave_MA", sizeof(drumsave_MA), 0, &drumsave_MA},
  {"Mode_drumsave_value", sizeof(Mode_drumsave_value), 0, &Mode_drumsave_value},
  {"drum_count", sizeof(drum_count), 0, &drum_count},
  {"drum_running", sizeof(drum_running), 0, &drum_running},
  {"drum_mode", sizeof(drum_mode), 0, &drum_mode},
  {"drum_address", sizeof(drum_address), 0, &drum_address},
  {"drum_cell_count", sizeof(drum_cell_count), 0, &drum_cell_count},
  {"YE_wait", sizeof(YE_wait), 0, &YE_wait},
  {"running", sizeof(running), 0, &running},
  {"spild", sizeof(spild), 0, &spild},
  {"h", sizeof(h), 0, &h},
  {"b", sizeof(b), 0, &b},
  {"c", sizeof(c), 0, &c},
  {"Select1", sizeof(selected_out[1]), 0, &selected_out[1]},
  {"Select2", sizeof(selected_out[2]), 0, &selected_out[2]},
  {"Select3", sizeof(selected_out[3]), 0, &selected_out[3]},
  {"Select4", sizeof(selected_out[4]), 0, &selected_out[4]},
  {"", 0, 0, NULL}
};

int GIER_save(char *filename)
{
  FILE *fp;
  int ifield,i,j;
  int progress;
  int max_progress;

  fp = fopen(filename, "w");

  if(fp == NULL) return 0;

  progress=0;
  max_progress = sizeof(fields)/sizeof(fields[0]) + 1024 + Tromle_size*40;
  if(Buffer != NULL)
  {
    max_progress += 4096;
    for(i=0; i<MAXBDISKS; i++)
    {
      if(BDisk_present[i])
      {
	max_progress += BDisk_size[i]*BDisk_tracksize[i];
      }
    }
  }

  progress_init(max_progress, "Saving file");

  if(Mode == Mode1) Mode_value=1;
  if(Mode == Mode2) Mode_value=2;
  if(Mode == Mode3) Mode_value=3;
  if(Mode == Mode4) Mode_value=4;
  if(Mode == Mode5) Mode_value=5;
  if(drumsave_Mode == Mode1) Mode_drumsave_value=1;
  if(drumsave_Mode == Mode2) Mode_drumsave_value=2;
  if(drumsave_Mode == Mode3) Mode_drumsave_value=3;
  if(drumsave_Mode == Mode4) Mode_drumsave_value=4;
  if(drumsave_Mode == Mode5) Mode_drumsave_value=5;
  kb1_visible = kb1_is_visible();
  kb2_visible = kb2_is_visible();
  typewriter_visible = typewriter_is_visible();
  reader_visible = reader_is_visible();
  punch_visible = punch_is_visible();
  printer_visible = printer_is_visible();
  plotter_visible = plotter_is_visible();
  nimbi_visible = nimbi_is_visible();

  for(ifield=0; fields[ifield].name[0] != '\0'; ifield++)
  {
    progress_setvalue(progress++);
    fprintf(fp, "%s\t", fields[ifield].name);
    if(fields[ifield].elementsize == 2)
      fprintf(fp, "%4.4hX\n", *(unsigned short *) (fields[ifield].ptr));
    if(fields[ifield].elementsize == 4)
      fprintf(fp, "%8.8lX\n", *(unsigned int *) (fields[ifield].ptr));
    if(fields[ifield].elementsize == 8)
#ifdef WINDOWS
      fprintf(fp, "%16.16I64X\n", *(unsigned long long *) (fields[ifield].ptr));
#else
      fprintf(fp, "%16.16llX\n", *(unsigned long long *) (fields[ifield].ptr));
#endif
  }

  for(i=0; i<1024; i++)
  {
    progress_setvalue(progress++);
    if(Ferrit[i] != 0ULL)
#ifdef WINDOWS
      fprintf(fp, "Ferrit[%d]\t%16.16I64X\n", i, Ferrit[i]);
#else
      fprintf(fp, "Ferrit[%d]\t%16.16llX\n", i, Ferrit[i]);
#endif
  }

  for(i=0; i<Tromle_size*40; i++)
  {
    progress_setvalue(progress++);
    if(Tromle[i] != 0ULL)
#ifdef WINDOWS
      fprintf(fp, "Tromle[%d]\t%16.16I64X\n", i, Tromle[i]);
#else
      fprintf(fp, "Tromle[%d]\t%16.16llX\n", i, Tromle[i]);
#endif
  }

  if(Buffer != NULL)
  {
    for(i=0; i<4096; i++)
    {
      progress_setvalue(progress++);
      if(Buffer[i] != 0ULL || i == 0)
#ifdef WINDOWS
	fprintf(fp, "Buffer[%d]\t%16.16I64X\n", i, Buffer[i]);
#else
	fprintf(fp, "Buffer[%d]\t%16.16llX\n", i, Buffer[i]);
#endif
    }
    for(i=0; i<MAXBDISKS; i++)
    {
      if(BDisk_present[i])
      {
        fprintf(fp, "BDisk%d_tracksize\t%8.8lX\n", i+8, BDisk_tracksize[i]);
        fprintf(fp, "BDisk%d_size\t%8.8lX\n", i+8, BDisk_size[i]);
	for(j=0; j<BDisk_size[i]*BDisk_tracksize[i]; j++)
	{
	  progress_setvalue(progress++);
	  if(BDisk[i][j] != 0ULL || j == 0)
	  {
#ifdef WINDOWS
	    fprintf(fp, "BDisk%d[%d]\t%16.16I64X\n", i+8, j, BDisk[i][j]);
#else
	    fprintf(fp, "BDisk%d[%d]\t%16.16llX\n", i+8, j, BDisk[i][j]);
#endif
	  }
	}
      }
    }
  }

  progress_stop();

  fclose(fp);
  return 1;
}

int GIER_load(char *filename)
{
  FILE *fp;
  int ifield, i, j, bdisk;
  char buf[1024];
  char *bp,*cp,*cp2;
  int error_occured;
  int iline;

  fprintf(stderr, "GIER_load: %s\n", filename);

  fp = fopen(filename, "r");

  if(fp == NULL) return 0;

  /* Store state as it is */
  kb1_visible = kb1_is_visible();
  kb2_visible = kb2_is_visible();
  typewriter_visible = typewriter_is_visible();
  reader_visible = reader_is_visible();
  punch_visible = punch_is_visible();
  printer_visible = printer_is_visible();
  plotter_visible = plotter_is_visible();
  nimbi_visible = nimbi_is_visible();

  selected_out[1] = 8;
  selected_out[2] = 16;
  selected_out[3] = 32;
  selected_out[4] = 64;

  for(ifield=0; fields[ifield].name[0] != '\0'; ifield++) fields[ifield].hasbeenread = 0;
  Mode_value = 0;
  TR_error = 0;
  LI_drum = 0ULL;
  TA=0;
  drumsave_MA=1;
  Mode_drumsave_value = 0;
  drum_count = 0;
  drum_running = 0;
  drum_mode = 0;
  drum_address = 0;
  drum_cell_count = 0;

  fseek(fp, 0, SEEK_END);
  progress_init(ftell(fp), "Loading file");
  fseek(fp, 0, SEEK_SET);
  iline=0;

  while(1)
  {
    bp = fgets(buf, sizeof(buf), fp);
    if(bp == NULL) break;

    if((iline++)%500==0)progress_setvalue(ftell(fp));

    cp=strtok(bp, "\t\n");
    if(!cp) continue;

    if(!strncmp(cp, "Ferrit[", 7))
    {
      sscanf(cp+7, "%d", &i);
      cp=strtok(0, "\t\n");
#ifdef WINDOWS
      sscanf(cp, "%I64x", Ferrit+i);
#else
      sscanf(cp, "%llx", Ferrit+i);
#endif
    }
    else if(!strncmp(cp, "Tromle[", 7))
    {
      sscanf(cp+7, "%d", &i);
      
      if(i > Tromle_size*40)
      {
        fprintf(stderr, "Tromle element is outside drum (%d > %d).\n", i, Tromle_size*40);
	fclose(fp);
	return 0;
      }
      cp=strtok(0, "\t\n");
#ifdef WINDOWS
      sscanf(cp, "%I64x", Tromle+i);
#else
      sscanf(cp, "%llx", Tromle+i);
#endif
    }
    else if(!strncmp(cp, "Buffer[", 7))
    {
      sscanf(cp+7, "%d", &i);
      
      if(i > 4095)
      {
        fprintf(stderr, "Buffer element is outside buffer (%d > 4095).\n", i);
	fclose(fp);
	return 0;
      }
      if(Buffer==NULL)
      {
        Buffer = (GIERword *) malloc(sizeof(*Buffer)*4096);
	for(j=0; j<4096; j++) Buffer[i] = 0ULL;
      }
      cp=strtok(0, "\t\n");
#ifdef WINDOWS
      sscanf(cp, "%I64x", Buffer+i);
#else
      sscanf(cp, "%llx", Buffer+i);
#endif
    }
    else if(!strncmp(cp, "BDisk", 5))
    {
      cp2 = cp+5;
      bdisk = *(cp2++)-'0';
      if(bdisk==1) bdisk=bdisk*10+*(cp2++)-'0';
      if(*cp2 == '_')
      {
	cp2++;
	cp=strtok(0, "\t\n");
	if(!strncmp(cp2, "tracksize", 9))
	{
	  sscanf(cp, "%lx", &BDisk_tracksize[bdisk-8]);
	}
	else if(!strncmp(cp2, "size", 4))
	{
	  sscanf(cp, "%lx", &BDisk_size[bdisk-8]);
	}
      }
      else if(*cp2 == '[')
      {
	cp2++;
	sscanf(cp2, "%d", &i);
	if(BDisk[bdisk-8]==NULL)
	{
	  if(BDisk_tracksize[bdisk-8]==0 || BDisk_size[bdisk-8]==0)
	  {
	    fprintf(stderr, "Buffer disk %d element present, but no tracksize or size present.\nAssuming CDC 854, 2030 tracks of 600 words", bdisk);
	    BDisk_tracksize[bdisk-8]=600;
	    BDisk_size[bdisk-8]=2030;
	  }
	  BDisk[bdisk-8] = (GIERword *) malloc(sizeof(**BDisk)*BDisk_tracksize[bdisk-8]*BDisk_size[bdisk-8]);
	  for(j=0; j<BDisk_tracksize[bdisk-8]*BDisk_size[bdisk-8]; j++)
	    BDisk[bdisk-8][j] = 0ULL;
          BDisk_present[i]=1;
	}
	if(i>=BDisk_tracksize[bdisk-8]*BDisk_size[bdisk-8])
	{
	  fprintf(stderr, "Buffer disk %d element %d outsize limit: %d\n", bdisk, i, BDisk_tracksize[bdisk-8]*BDisk_size[bdisk-8]);
	  fclose(fp);
	  return 0;
	}
	cp=strtok(0, "\t\n");
#ifdef WINDOWS
	sscanf(cp, "%I64x", &BDisk[bdisk-8][i]);
#else
	sscanf(cp, "%llx", &BDisk[bdisk-8][i]);
#endif
      }
    }
    else
    {
      for(ifield=0; fields[ifield].name[0] != '\0'; ifield++)
      {
        if(!strcmp(cp, fields[ifield].name))
	{
	  cp=strtok(0, "\t\n");
	  if(fields[ifield].elementsize == 2)
	  {
            sscanf(cp, "%hx", (unsigned short *) (fields[ifield].ptr));
	  }
	  else if(fields[ifield].elementsize == 4)
	  {
            sscanf(cp, "%x", (unsigned int *) (fields[ifield].ptr));
	  }
	  else if(fields[ifield].elementsize == 8)
	  {
#ifdef WINDOWS
            sscanf(cp, "%I64x", (unsigned long long *) (fields[ifield].ptr));
#else
            sscanf(cp, "%llx", (unsigned long long *) (fields[ifield].ptr));
#endif
	  }
	  fields[ifield].hasbeenread = 1;
          if(!strcmp(fields[ifield].name,"Tromle_size"))
	  {
	    free((char *) Tromle);
	    Tromle=(GIERword *) malloc(sizeof(*Tromle)*40*Tromle_size);
	    for(i=0; i<Tromle_size*40; i++) Tromle[i] = 0ULL;
	    /* We also clear the core store */
	    for(i=0; i<1024; i++) Ferrit[i] = 0ULL;
	  }
	}
      }
    }
  }
  
  progress_stop();
  fclose(fp);

  checkdebug();

  if(Mode_value == 1) Mode=Mode1;
  if(Mode_value == 2) Mode=Mode2;
  if(Mode_value == 3) Mode=Mode3;
  if(Mode_value == 4) Mode=Mode4;
  if(Mode_value == 5) Mode=Mode5;

  for(ifield=0; fields[ifield].name[0] != '\0'; ifield++)
  {
    if(!fields[ifield].hasbeenread)
    {
      memset((char *) (fields[ifield].ptr), 0, fields[ifield].elementsize);
    }
  }

  if(kb2_visible != kb2_is_visible())
  {
    if(kb2_visible)
    {
      kb2_init();
    }
    else
    {
      kb2_destroy();
    }
  }

  if(kb1_visible != kb1_is_visible())
  {
    if(kb1_visible)
    {
      kb1_init();
    }
    else
    {
      kb1_destroy();
    }
  }

  if(typewriter_visible != typewriter_is_visible())
  {
    if(typewriter_visible)
    {
      typewriter_init();
    }
    else
    {
      typewriter_destroy();
    }
  }

  if(reader_visible != reader_is_visible())
  {
    if(reader_visible)
    {
      reader_init();
    }
    else
    {
      reader_destroy();
    }
  }

  if(punch_visible != punch_is_visible())
  {
    if(punch_visible)
    {
      punch_init();
    }
    else
    {
      punch_destroy();
    }
  }

  if(printer_visible != printer_is_visible())
  {
    if(printer_visible)
    {
      printer_init();
    }
    else
    {
      printer_destroy();
    }
  }

  if(plotter_visible != plotter_is_visible())
  {
    if(plotter_visible)
    {
      plotter_init();
    }
    else
    {
      plotter_destroy();
    }
  }

  if(nimbi_visible != nimbi_is_visible())
  {
    if(nimbi_visible)
    {
      nimbi_init();
    }
    else
    {
      nimbi_destroy();
    }
  }


  if(YE_wait)
  {
    if(((BY&2)&&(!(BY&1)))||((!(BY&2))&&(BY&1)))
    {
      typewriter_wait(TRUE);
    }
    else
    {
      reader_wait(TRUE);
    }
  }

  return 1;
}
