// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// File: main.cpp                                               |
// Purpose: program for visualizing artificial neural networks  |
// Author: Taivo Lints, Estonia                                 |
// Date: May, 2003                                              |
// Copyright: see copyright.txt                                 |
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

#include "allegro.h"
#include "visnet/visual_ann.h"
#include "visnet/mypalette.h"
#include "stdio.h"  // For sprintf() and strcat() (Allegro needs char* -s)
#include <vector>
using namespace std;

int main() {

  // ******************************
  // * Setting up the environment *
  // ******************************

  allegro_init();
  install_keyboard();
  install_mouse();
  install_timer();

  set_color_depth(8);
  set_gfx_mode(GFX_AUTODETECT, 800, 600, 0, 0);

  // Configures the text to be drawn with transparent background.
  text_mode(-1);

  // Sets the color of warning texts.
  int color_of_wtxt = 90;
  change_color(color_of_wtxt, 63, 5, 0);

  // Sets the color of notice texts.
  int color_of_ntxt = 91;
  change_color(color_of_ntxt, 55, 45, 10);

  // Sets colors for file loading menu.
  int color_of_gui_bg = 92,
      color_of_gui_fg = 93;

  change_color(color_of_gui_bg, 0, 0, 20);
  change_color(color_of_gui_fg, 0, 50, 0);

  gui_bg_color = color_of_gui_bg;
  gui_fg_color = color_of_gui_fg;

  // Sets colors for help background.
  int color_of_help_bg = 94;
  change_color(color_of_help_bg, 0, 0, 10);

  // Creates a bitmap object for double buffering (to avoid screen flickering).
  BITMAP* dblbuf = create_bitmap(SCREEN_W, SCREEN_H);
  clear_bitmap(dblbuf);


  // ********************************
  // * Initializing other variables *
  // ********************************

  // Initializes variables for loading the configuration file.
  char config_file[500];
  sprintf(config_file, "config_generated.txt");

  char message[] = "Load new configuration file";  // Message for load menu.
  char* ext = "TXT";   // File filter for load menu.

  char help_msg1[150];  // Creating some help messages.
  sprintf(help_msg1, "File ");
  strcat(help_msg1, config_file);
  strcat(help_msg1, " is missing");

  char help_msg2[150];
  sprintf(help_msg2, "Quit, fix ");
  strcat(help_msg2, config_file);
  strcat(help_msg2, " and restart program");

  // Creates a visual network object.
  VisualANN* pVisNet = new VisualANN(config_file);
 
  // Gets the number of input nodes in network. This value is needed
  // for processing the user input.
  int num_of_inps = pVisNet->get_number_of_inputs();

  // How many of these inputs are accessible by user? If you change this
  // number, you MUST also change the code in "Processing user interaction"
  // (in the "switch" statement).
  int user_accessible_inps = 10;

  // Creates a vector for feeding inputs into the network (vector size
  // is num_of_inps and vector is filled with zeros).
  vector<double> v_dbl_inpvalues(num_of_inps, 0);

  // Now cuts down the num_of_inps value because of the way this
  // value is used in a "switch" statement later on.
  if(num_of_inps > user_accessible_inps)
    num_of_inps = user_accessible_inps;

  // Some flags noticing when to print additional help for user.
  bool flag_no_inputs = false,
       flag_just_started = true;

  if(num_of_inps == 0)
    flag_no_inputs = true;

  // Program loops while this flag is "true".
  bool keepgoing = true;
 

  // *****************
  // * The main loop *
  // *****************

  while(keepgoing) {

    //  ----------------------------
    // | Updating & drawing network |
    //  ----------------------------

    pVisNet->update();
    pVisNet->draw(dblbuf);


    //  -------------------------------
    // | Printing some additional help |
    //  -------------------------------

    if(flag_no_inputs) {
      textout(dblbuf, font, "Network has no inputs!", 2, 10, color_of_wtxt);

      if(flag_just_started) {
        textout(dblbuf, font, "Possible reasons:", 15, 30, color_of_wtxt);
        textout(dblbuf, font, help_msg1, 35, 42, color_of_wtxt);
        textout(dblbuf, font, "or is incorrect", 35, 54, color_of_wtxt);
        textout(dblbuf, font, "Solutions:", 15, 74, color_of_wtxt);
        textout(dblbuf, font, help_msg2, 35, 86, color_of_wtxt);
        textout(dblbuf, font,
          "or press L and load another configuration file", 35, 98, color_of_wtxt);
      }
      else {
        textout(dblbuf, font, "Possible reasons:", 15, 30, color_of_wtxt);
        textout(dblbuf, font, "Loaded file is incorrect", 35, 42, color_of_wtxt);
        textout(dblbuf, font,
          "or path contains non-english characters", 35, 54, color_of_wtxt);
        textout(dblbuf, font, "Solutions:", 15, 74, color_of_wtxt);
        textout(dblbuf, font, "Fix your file and reload it", 35, 86, color_of_wtxt);
        textout(dblbuf, font,
          "or press L and load another configuration file", 35, 98, color_of_wtxt);
        textout(dblbuf, font,
          "or in case of incorrect path move your file into", 35, 110, color_of_wtxt);
        textout(dblbuf, font, "correct path and load again", 35, 122, color_of_wtxt);
        textout(dblbuf, font, "Your selected path was:", 15, 140, color_of_ntxt);
        textout(dblbuf, font, config_file, 35, 152, color_of_ntxt);
      }
    }

    
    //  -----------------------------
    // | Processing user interaction |
    //  -----------------------------

    // - - Setting network inputs - - -

    // Gets mouse y value and normalizes it.
    double vertical_input = static_cast<double>(mouse_y);
    vertical_input = 1 - vertical_input / (SCREEN_H - 1);

    // Now here's an interesting statement. It sets values for feeding
    // into network. To avoid crashing when there are less than 10 inputs
    // in network (by indexing out of the vector), it jumps / switches
    // to the right place. But as there are no "break"-s at the ends of
    // lines, then it will go all the way down through "case"-s following
    // the one it jumped to.
    
    switch(num_of_inps) {
      case 10 : if(key[KEY_0]) v_dbl_inpvalues[9] = vertical_input;
      case  9 : if(key[KEY_9]) v_dbl_inpvalues[8] = vertical_input;
      case  8 : if(key[KEY_8]) v_dbl_inpvalues[7] = vertical_input;
      case  7 : if(key[KEY_7]) v_dbl_inpvalues[6] = vertical_input;
      case  6 : if(key[KEY_6]) v_dbl_inpvalues[5] = vertical_input;
      case  5 : if(key[KEY_5]) v_dbl_inpvalues[4] = vertical_input;
      case  4 : if(key[KEY_4]) v_dbl_inpvalues[3] = vertical_input;
      case  3 : if(key[KEY_3]) v_dbl_inpvalues[2] = vertical_input;
      case  2 : if(key[KEY_2]) v_dbl_inpvalues[1] = vertical_input;
      case  1 : if(key[KEY_1]) v_dbl_inpvalues[0] = vertical_input;
      default:;
    }

    // And finally feeds the vector into network.
    pVisNet->set_inputs(&v_dbl_inpvalues);


    // - - Loading new network - - -

    if(key[KEY_L]) {

      // Calls file selection menu.
      int i = file_select_ex(message, config_file, ext, 490, 450, 500);
      rest(250);  // In case menu was exited by pressing Esc, this avoids
                  // quitting the program.

      // If user chose something, then makes a new network of it.
      if(i != 0) {
        delete pVisNet;
        pVisNet = new VisualANN(config_file);

        num_of_inps = pVisNet->get_number_of_inputs();

        v_dbl_inpvalues.assign(num_of_inps, 0);  // Resize & write zeros.

        if(num_of_inps > user_accessible_inps)
          num_of_inps = user_accessible_inps;

        flag_just_started = false;

        if(num_of_inps == 0)
          flag_no_inputs = true;
        else
          flag_no_inputs = false;
      }
    }

    // - - Other user interactions - - -

    // Press Escape to exit.
    if(key[KEY_ESC]) {
      text_mode(0);
      textout(screen, font, "ESC pressed, will QUIT", 2, 2, color_of_wtxt);
      rest(2000);
      keepgoing = false;
    }

    // Press F1 for help.
    if(key[KEY_F1]) {
      rectfill(screen, 0, 0, 385, 164, color_of_help_bg);

      textout(screen, font, "Keys:", 5, 8, color_of_ntxt);
      textout(screen, font, "1 + mouse up/down  - change first input", 15, 30, color_of_ntxt);
      textout(screen, font, "2 + mouse up/down  - change second input", 15, 42, color_of_ntxt);
      textout(screen, font, ".. + mouse up/down - change .. input", 15, 54, color_of_ntxt);
      textout(screen, font, "L   - load network", 15, 78, color_of_ntxt);
      textout(screen, font, "F1  - this help", 15, 102, color_of_ntxt);
      textout(screen, font,
        "F12 - save screenshot (does NOT work in menus)", 15, 114, color_of_ntxt);
      textout(screen, font, "Esc - quit program", 15, 126, color_of_ntxt);
      textout(screen, font, "Press any key to leave help", 15, 150, color_of_ntxt);

      rest(100);
      clear_keybuf();
      while(!keypressed());
      rest(250);
    }

    // Press F12 to save screenshot.
    if(key[KEY_F12]) {
      PALETTE pal;
      get_palette(pal);
      save_bitmap("screenshot.bmp", dblbuf, pal);
      textout(screen, font, "Screenshot saved", 2, 2, color_of_ntxt);
      rest(2000);
    }


    //  ----------
    // | Blitting |
    //  ----------

    // Copies the dblbuf into video memory at the right moment
    // (when vertical retrace is beginning in monitor).
    vsync();
    blit(dblbuf, screen, 0, 0, 0, 0, SCREEN_W, SCREEN_H);
    clear_bitmap(dblbuf);

  }


  // ***************
  // * Cleaning up *
  // ***************

  destroy_bitmap(dblbuf);

}

END_OF_MAIN();