// Mavica Declarations ************************************************
const char* stateFile="mavica.state";
int mavica_initialized = FALSE;
struct camera_status {
	int busy;
	int exposure_1;
	int exposure_2;
	int flash_1;
	int flash_2;
	int error;
} mavica_status = {0,0,0,0,0,0};

int bootCounter;
FILE *f;
int zoom = -1;
float zoom_settings[9] = {1.0, 1.1, 1.3, 1.5, 1.7, 1.9, 2.2, 2.6, 3.0};
//int zoom_out_delays[9] = { 32,  32,  32,  32,  48,  64,  64,  64,  64}; // uses initial index
int zoom_out_delays[9] = { 64,  64,  64,  64,  96, 128, 128, 128, 128}; // uses initial index
int zoom_in_delays[9]  = { 32,  32,  32,  32,  32,  32,  48,  64,  64};
int onoff = -1;
int shots = -1;

void initialize_mavica();
void save_camera_status(int data[]);
void show_camera_status();
void read_mavica_bytes(int count);
void send_mavica_command(int count, unsigned char cmd);

void mavica_init(){
  char key[1015] = {'u','n','k','n','o','w','n','\x0'};
  char value[6];

  f = fopen(stateFile,"r+");
  if (!f){
    printf("Unable to open \"%s\" file\n", stateFile);
    exit(-1);
  }
  while (!feof(f)){
    fscanf(f,"%s%s", key, value);
    if (!strncmp(key,"zoom", 4)){
      zoom = atoi(value);
      if (zoom < 0)
        zoom = 0;
      else if (zoom > 8)
        zoom = 8;
    }
    else if (!strncmp(key,"onoff", 5))
      onoff = atoi(value);
    else if (!strncmp(key,"shots", 9))
      shots = atoi(value);
    else if (!strncmp(key,"unknown", 7));
    else{
      printf("Unknown key \"%s\" in state file \"%s\"\n", key, stateFile); 
      exit(-1);
    }
  }
  printf("zoom=%d\n", zoom);
  printf("onoff=%d\n", onoff);
  if (zoom == -1)
    zoom = 2;              // Assume camera is at initially at zoom of 1.3
  if (onoff == -1)
    onoff = 0;             // Assume camera is turned off at start
  if (shots == -1)
    shots = 0;             // Assume no  pictures are on the CD

  mavica_initialized = true;
}

void mavica_deinit(){
  if (f){
    rewind(f);
    fprintf(f,"%s %d\n", "zoom", zoom);
    fprintf(f,"%s %d\n", "onoff", onoff);
    fprintf(f,"%s %d\n", "shots", shots);
    fclose(f);
  }
}

void initialize_mavica(){
  int loops_per_bit;
  if (!mavica_initialized)
    mavica_init();
  pthread_mutex_lock(&dlp_command_mutex);
  outbuf[1] = MavicaInit;
  send_dlp_command();
  loops_per_bit = inbuf[0];
  pthread_mutex_unlock(&dlp_command_mutex);
  pthread_mutex_lock(&verbose_mutex);
  if (verbose){
    if (loops_per_bit)
      printf("Mavica has been turned on.  Loops_per_bit = %d\n", loops_per_bit);
    else
      printf("Mavica was already on.\n");
  }
  pthread_mutex_unlock(&verbose_mutex);
  if (loops_per_bit && !(loops_per_bit > 30 && loops_per_bit < 40) && !(loops_per_bit > 155 && loops_per_bit < 165)){
    printf("Unreasonable value of %d for Loops_per_bit.\n", loops_per_bit);
    exit(-1);
  }
  bootCounter = 4;
  printf("%1d... ", bootCounter--);
}

void save_camera_status(int data[]){
    if (data[3] == 0x00 && data[4] == 0x00){
      if (data[0] == 0x00 && data[1] == 0x00 && data[2] == 0xf0){
        if (bootCounter == 3){
          printf("%1d... \n", bootCounter);
	  bootCounter = 2;
	}
	return;
      }
      if (data[1] == 0x7f && (data[2] == 0x06 || data[2] == 0x0e || data[2] == 0x01 || data[2] == 0x0a)){
        if (bootCounter == 2){
          printf("%1d... ", bootCounter);
	  bootCounter = 1;
	}
	return;
      }
      if (data[1] == 0x77 && (data[2] == 0x06 || data[2] == 0x0e || data[2] == 0x01 || data[2] == 0x0a)){
        if (bootCounter == 1){
          printf("%1d... ", bootCounter);
	  bootCounter = 0;
	}
        return;
      }
    }
    if (data[1] != 0x77 || (data[0] != 0x00 && data[0] != 0x80)){
      printf("Unexpected sequence: %02x %02x %02x %02x %02x\n", data[0], data[1], data[2], data[3], data[4]);
//      exit(-1);
    }
    else{
      switch (bootCounter){
        case 3:
          printf("3... ");
        case 2:
          printf("2... ");
        case 1:
	  printf("1... ");
	case 0:
          printf("0... Boot complete\n");
	  break;
	case -1:
	  break;
        default:
	  printf("Impossible BootCounter value of %d\n",bootCounter);
	  exit(-1);
      }
      bootCounter = -1;
    }
    if ((data[2] & 0x90) == 0x90)
      mavica_status.busy=1;
    else
      mavica_status.busy=0;
    if ((data[2] & 0xef) == 0x86){
      mavica_status.exposure_1 = data[3];
      mavica_status.exposure_2 = data[4];
    }
    if ((data[2] & 0xef) == 0x81){
      mavica_status.flash_1 = data[3];
      mavica_status.flash_2 = data[4];
    }
    if ((data[2] & 0xef) == 0x8e){
      mavica_status.error = data[4];
      if (data[3] != 0x15){
        printf("Unexpected 0x8e/0x9e sequence: %02x %02x %02x\n", data[2], data[3], data[4]);
        exit(-1);
      }
    }
    if ((data[2] & 0xef) == 0x8a && data[3] != 0x80 && data[4] != 0xc0){
      printf("Unexpected 0x8a/0x9a sequence: %02x %02x %02x\n", data[2], data[3], data[4]);
      exit(-1);
    }
}


void read_mavica_bytes(int count){
  int i,j;
  int data[2056];
  int group;
  pthread_mutex_lock(&dlp_command_mutex);
  outbuf[1] = MavicaBytes;
  outbuf[2] = count;
  send_dlp_command();
  if (!count) count = 256;
  for (i=0; i<8*count; i++)
    data[i]=inbuf[i];
  pthread_mutex_unlock(&dlp_command_mutex);
  for (i=0; i<8*count; i+=8){
    save_camera_status(&data[i+3]);

//    show_camera_status();
  }
  pthread_mutex_lock(&verbose_mutex);
  if (verbose){
    switch (data[5] & 0x0f){
      case 0x06:
        j=0;
	break;
      case 0x0e:
	j=24;
	break;
      case 0x01:
	j=48;
	break;
      case 0x0a:
	j=72;
	break;
      default:
        if (data[5] == 0xf0){
          j = 0;
          break;
        }
	printf("Unknown value for byte 6: %02x\n", data[5]);
    }
    group = 0;
    for (i=0; i<j; i++)
      printf(" ");
    for (i=0; i<count; i++){
      if ( ( ((data[8*i] & 0xf0) ==0x70 || (data[8*i] == 0x00)) && (data[8*i+5] & 0xef) == 0x86 ) || (data[8*i+5] == 0xf0 && group == 4) ){
        printf("\n");
	group = 0;
      }
      group++;
      for (j=0; j<8; j++)
        printf("%02x ",data[j+8*i]);
    }
    printf("\n");
    for (i=0; i<8*count; i+=8){
      if ((data[i+5] == 0x96) && i>31){
        if ((data[i+6] & 0x01) && !(data[i-26] & 0x01))
          printf("Aperature locked\n");
        if (!(data[i+6] & 0x01) && (data[i-26] & 0x01))
          printf("Aperature unlocked\n");
	if ((data[i+7] & 0x04) && !(data[i-25] & 0x04))
          printf("Shutter speed locked\n");
	if (!(data[i+7] & 0x04) && (data[i-25] & 0x04))
          printf("Shutter speed unlocked\n");
      }
      else if ((data[i+5] == 0x91) && i>31 && (data[i+6] != data[i-26] || data[i+7] != data[i-25]))
	  printf("Flashed\n");
      if ((data[i+5] & 0x10) && i>7 && !(data[i-3] & 0x10))
	  printf("Processing started\n");
      if (!(data[i+5] & 0x10) && i>7 && (data[i-3] & 0x10))
	  printf("Processing finished\n");
    }
  }
  pthread_mutex_unlock(&verbose_mutex);
}

void send_mavica_command(int count, unsigned char cmd){
  int i,j,k;
  int data[1288];
  pthread_mutex_lock(&dlp_command_mutex);
  outbuf[1] = MavicaCommand;
  outbuf[2] = count;
  outbuf[3] = cmd;
  send_dlp_command();
  if (!count) count = 256;
  for (i=0; i<5*count; i++)
    data[i]=inbuf[i];
  pthread_mutex_unlock(&dlp_command_mutex);
  for (i=0; i<5*count; i+=5)
    save_camera_status(&data[i]);
  pthread_mutex_lock(&verbose_mutex);
  if (verbose && bootCounter <= 0){
    switch (data[2] & 0x0f){
      case 0x06:
        k=0;
        break;
      case 0x0e:
        k=15;
        break;
      case 0x01:
        k=30;
        break;
      case 0x0a:
        k=45;
        break;
      default:
        if (data[2] == 0xf0){
          k = 0;
          break;
        }
        printf("Unknown value for byte 2: %02x\n", data[2]);
        exit(-1);
      }
      for (j=0; j<k; j++){
        printf(" ");
      }
      for (i=0; i<count; i++){
        if (((data[5*i+1] & 0xf7) == 0x77 && (data[5*i+2] & 0x6f) == 0x06))
          printf("\n");
        for (j=0; j<5; j++)
          printf("%02x ",data[j+5*i]);
        if (data[5*i+2] == 0xf0)
          printf("\n");
      }
    printf("\n");
    for (i=0; i<5*count; i+=5){
      if ((data[i+2] == 0x96) && i>19){
        if ((data[i+3] & 0x01) && !(data[i-17] & 0x01))
          printf("Aperature locked\n");
        if (!(data[i+3] & 0x01) && (data[i-17] & 0x01))
          printf("Aperature unlocked\n");
	if ((data[i+4] & 0x04) && !(data[i-16] & 0x04))
          printf("Shutter speed locked\n");
	if (!(data[i+4] & 0x04) && (data[i-16] & 0x04))
          printf("Shutter speed unlocked\n");
      }
      else if ((data[i+2] == 0x91) && i>19 && (data[i+3] != data[i-17] || data[i+4] != data[i-16]))
	  printf("Flashed\n");
      if (i>4 && (data[i-4] == 0x77) && (data[i+1] == 0x77) && (data[i+2] & 0x10) && !(data[i-3] & 0x10))
	  printf("Processing started\n");
      if (i>4 && (data[i-4] == 0x77) && (data[i+1] == 0x77) && !(data[i+2] & 0x10) && (data[i-3] & 0x10))
	  printf("Processing finished\n");
    }
  }
  pthread_mutex_unlock(&verbose_mutex);
}

void show_camera_status(){
  if (mavica_status.error)
    printf("ERROR %02x ", mavica_status.error);
  if (mavica_status.busy)
    printf("Camera busy... ");
  printf("exposure: %02x %02x ", mavica_status.exposure_1, mavica_status.exposure_2);
  if (mavica_status.exposure_1 != 0x00 || mavica_status.exposure_2 != 0x00){
    printf("Shutter set to ");
    switch (mavica_status.exposure_2 & 0xf8){
      case 0x18:
        printf("7 or 8 sec");
        break;
      case 0xe8:
        printf("3, 4, or 5 sec");
        break;
      case 0x68:
        printf("1.6, 2.0, or 2.5 sec");
        break;
      case 0xa8:
        printf("1/1.3, 1/1, or 1.3 sec");
        break;
      case 0x28:
        printf("1/2.5, 1/4, or 1/3 sec");
        break;
      case 0xc8:
        printf("1/5, 1/4, or 1/3 sec");
        break;
      case 0x48:
        printf("1/10, 1/8, or 1/6 sec");
        break;
      case 0x88:
        printf("1/20, 1/15, or 1/13 sec");
        break;
      case 0x08:
        printf("1/40, 1/30, or 1/25 sec");
        break;
      case 0x00:
        printf("1/60 or 1/50 sec");
        break;
      case 0x80:
        printf("1/80 sec");
        break;
      case 0x40:
        printf("1/100 sec");
        break;
      case 0xc0:
        printf("1/125 sec");
	break;
      case 0x20:
        printf("1/160 sec");
	break;
      case 0xa0:
        printf("1/200 sec");
	break;
      case 0x60:
        printf("1/250 sec");
        break;
      case 0xe0:
        printf("1/320 sec");
	break;
      case 0x10:
        printf("1/400 sec");
	break;
      case 0x90:
        printf("1/500 sec");
	break;
      case 0x50:
        printf("1/640 sec");
	break;
      case 0xd0:
        printf("1/800 sec");
	break;
      case 0x30:
        printf("1/1000 sec");
        break;
      default:
	printf("Unknown shutter speed %02x\n", mavica_status.exposure_2);
	exit(-1);
    }
    printf(" @ ");
    switch ((mavica_status.exposure_1 & 0xf0) + (mavica_status.exposure_2 & 0x02)){
      case 0x60:
        printf("f2.0");
        break;
      case 0xe0:
        printf("f2.2");
        break;
      case 0x10:
        printf("f2.5");
        break;
      case 0x90:
        printf("f2.8");
	break;
      case 0x50:
        printf("f3.2");
	break;
      case 0xd0:
	printf("f3.5");
        break;
      case 0x30:
        printf("f4.0");
	break;
      case 0xb0:
        printf("f4.5");
	break;
      case 0x70:
        printf("f5.0");
	break;
      case 0xf0:
        printf("f5.6");
	break;
      case 0x02:
        printf("f6.3");
	break;
      case 0x82:
        printf("f7.1");
	break;
      case 0x42:
        printf("f8.0");
	break;
      default:
	printf("Unknown f-stop setting: %02x\n", (mavica_status.exposure_1 & 0xf0) + (mavica_status.exposure_2 & 0x02));
	exit(-1);
    }
  }
  if (mavica_status.flash_1 != 0x00 || mavica_status.flash_2 != 0x00)
    printf(", flash: %02x %02x", mavica_status.flash_1, mavica_status.flash_2);
  else
    printf(", flash not used");
  printf(", Zoom at %2.1f", zoom_settings[zoom]);
  printf("\n");
  if ((mavica_status.exposure_1 & 0x0f) != 0x0c)
    printf("mavica_status.exposure_1 %02x has lower nibble %01x that is not 0xc.\n",mavica_status.exposure_1, mavica_status.exposure_1 & 0x0f);
  if ((mavica_status.exposure_2 & 0x05) != 0x00)
    printf("mavica_status.exposure_2 %02x has bit 0 and/or bit 2 set in a surprising manner.\n",mavica_status.exposure_2);
}

int indexFromZoom(char* z){
  if (strlen(z)==1){
    switch (atoi(z)){
      case 1:
        return 0;
	break;
      case 2:
        return 5;
	break;
      case 3:
	return 8;
	break;
      default:
	printf("Single digit zoom value should be 1, 2, or 3\n");
	exit(-1);
    }
  }
  else if (strlen(z) == 3){
    z[1]=z[2];
    z[2]='\x0';
    switch (atoi(z)){
      case 10:
        return 0;
        break;
      case 11:
        return 1;
        break;
      case 13:
        return 2;
        break;
      case 15:
        return 3;
        break;
      case 17:
        return 4;
        break;
      case 19:
        return 5;
        break;
      case 22:
        return 6;
        break;
      case 26:
        return 7;
        break;
      case 30:
        return 8;
        break;
    }
  }
  printf("Zoom value should be 1.0, 1.1, 1.3, 1.5, 1.7, 1.9, 2.2, 2.6, or 3.0, not %s\n", z);
  exit(-1);
}

void mavica(int argc, char** argv) {
  int i,j,k;
  verbose = true;
  if (argc==1){
    printf("  The following commands are available:\n"\
	   "      focus            Minimal time for autofocus\n"\
	   "      focushold <n>    Extra long time for autofocus - n seconds \n"\
	   "      click            Take a picture\n"\
           "      clickhold <n>    Take a picture and hold it n seconds in viewfinder\n"\
           "	  wider            One step more toward wider angle zoom\n"\
           "	  teler            One step more toward telephoto zoom\n"\
           "	  off              Turn camera off when done storing image\n"\
           "	  idle <n>         Wait for n 1/60-second intervals\n"\
	   "      read <n>         Monitor activity of another remote for n 1/60-second intervals\n"\
	   "  or a zoom ratio of\n"\
	   "    1.0 | 1.1 | 1.3 | 1.5 | 1.7 | 1.9 | 2.2 | 2.6 | 3.0\n\n"\
	   "(The resolution should be set to 5Mpixels with \"smart zoom\" off.)\n");
    exit(-1);
  }
  else{
    for (i=1; i<argc; i++){
      printf("**** %s ****\n", argv[i]);
      if (i == 1){
        check_id((char*)dlpId);
        initialize_mavica();
        zoom = 2;
	  if (strncmp(argv[i], "read", 4))
            send_mavica_command(128,'\x00');
	if (strncmp(argv[i], "read", 4))
          send_mavica_command(128,'\x00');
      }
      if (!strncmp(argv[i], "focushold",9)){
	for (j = 0; j < atoi(argv[i+1]); j++){
          send_mavica_command(60,'\x4a');
	}
	i++;
      }
      else if (!strncmp(argv[i], "focus",5))
        send_mavica_command(120,'\x4a');
      else if (!strncmp(argv[i], "clickhold",9)){
	for (j = 0; j < atoi(argv[i+1]); j++){
	  printf("j=%d ",j);
          send_mavica_command(60,'\x5a');
	}
	printf("\n");
	shots++;
	i++;
      }
      else if (!strncmp(argv[i], "click",5)){
        send_mavica_command(16,'\x5a');
	shots++;
      }
//      else if (!strncmp(argv[i], "full-wide",9))
//        send_mavica_command(atoi(argv[i+1]),'\x59');
//      else if (!strncmp(argv[i], "full-tele",9))
//        send_mavica_command(atoi(argv[i+1]),'\x19');
      else if (!strncmp(argv[i], "wider",5)){
        send_mavica_command(4,'\x69');
        send_mavica_command(zoom_in_delays[zoom],'\x00');
	zoom--;
        if (zoom < 0)
          zoom = 0;
      }
      else if (!strncmp(argv[i], "teler",5)){
        send_mavica_command(4,'\x29');
        send_mavica_command(zoom_out_delays[zoom],'\x00');
	zoom++;
        if (zoom > 8)
          zoom = 8;
      }
      else if (!strncmp(argv[i], "off",3)){
        send_mavica_command(5,'\x7a');
	onoff = 0;
      }
      else if (!strncmp(argv[i], "idle",4)){
        j = atoi(argv[i+1]);
	if (j == 0)
          j = 256;
        if (j > 128){
          send_mavica_command(128,'\x00');
          send_mavica_command(j-128,'\x00');
	}
	else
          send_mavica_command(j,'\x00');
	i++;
      }
      else if (!strncmp(argv[i], "read",4)){
        j = atoi(argv[i+1]);
	if (j == 0)
          j = 256;
        if (j > 128){
          read_mavica_bytes(128);
          read_mavica_bytes(j-128);
	}
        else
          read_mavica_bytes(j);
	i++;
      }
      else{
        j = indexFromZoom(argv[i]) - zoom;
	if (j < 0){
          for (k = 0; k < -j; k++){
            send_mavica_command(4,'\x69');
            send_mavica_command(zoom_in_delays[zoom],'\x00');
	    zoom--;
	  }
	}
	else if (j > 0){
          for (k = 0; k < j; k++){
            send_mavica_command(4,'\x29');
            send_mavica_command(4,'\x19');
            send_mavica_command(zoom_out_delays[zoom],'\x00');
	    zoom++;
	  }
	}
      }
      show_camera_status();
    }
  }
  mavica_deinit();   // deinit() needs to be done at end except when scope() is run
  dlp_deinit();
}


