///////////////////////////////////////////////////////////////////////
// File:        varabled.cpp
// Description: Variables Editor
// Author:      Joern Wanke
// Created:     Wed Jul 18 10:05:01 PDT 2007
//
// (C) Copyright 2007, Google Inc.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
///////////////////////////////////////////////////////////////////////
//
// The variables editor is used to edit all the variables used within
// tesseract from the ui.
#ifndef GRAPHICS_DISABLED
#include "varabled.h"

#ifdef WIN32
#else
#include <stdlib.h>
#include <stdio.h>
#endif

#include <map>

#include "scrollview.h"
#include "svmnode.h"

#include "varable.h"
#include "mainblk.h"

#define VARDIR        "configs/" /*variables files */
#define MAX_ITEMS_IN_SUBMENU 30

const ERRCODE NO_VARIABLES_TO_EDIT = "No Variables defined to edit";

// Contains the mappings from unique VC ids to their actual pointers.
static std::map<int, VariableContent*> vcMap;

static int nrVariables = 0;
static int writeCommands[2];

ELISTIZE(VariableContent)

// Constructors for the various VarTypes.
VariableContent::VariableContent(STRING_VARIABLE* it) {
  my_id_ = nrVariables;
  nrVariables++;
  var_type_ = VT_STRING;
  sIt = it;
  vcMap[my_id_] = this;
}
// Constructors for the various VarTypes.
VariableContent::VariableContent(INT_VARIABLE* it) {
  my_id_ = nrVariables;
  nrVariables++;
  var_type_ = VT_INTEGER;
  iIt = it;
  vcMap[my_id_] = this;
}
// Constructors for the various VarTypes.
VariableContent::VariableContent(BOOL_VARIABLE* it) {
  my_id_ = nrVariables;
  nrVariables++;
  var_type_ = VT_BOOLEAN;
  bIt = it;
  vcMap[my_id_] = this;
}
// Constructors for the various VarTypes.
VariableContent::VariableContent(double_VARIABLE* it) {
  my_id_ = nrVariables;
  nrVariables++;
  var_type_ = VT_DOUBLE;
  dIt = it;
  vcMap[my_id_] = this;
}

// Gets a VC object identified by its ID.
VariableContent* VariableContent::GetVariableContentById(int id) {
  return vcMap[id];
}

// Copy the first N words from the source string to the target string.
// Words are delimited by "_".
void VariablesEditor::GetFirstWords(
                     const char *s,  // source string
                     int n,          // number of words
                     char *t         // target string
                    ) {
  int full_length = strlen(s);
  int reqd_len = 0;              // No. of chars requird
  const char *next_word = s;

  while ((n > 0) && reqd_len < full_length) {
    reqd_len += strcspn(next_word, "_") + 1;
    next_word += reqd_len;
    n--;
  }
  strncpy(t, s, reqd_len);
  t[reqd_len] = '\0';            // ensure null terminal
}

// Getter for the name.
const char* VariableContent::GetName() const {
  if (var_type_ == VT_INTEGER) { return iIt->name_str(); }
  else if (var_type_ == VT_BOOLEAN) { return bIt->name_str(); }
  else if (var_type_ == VT_DOUBLE) { return dIt->name_str(); }
  else if (var_type_ == VT_STRING) { return sIt->name_str(); }
  else
    return "ERROR: VariableContent::GetName()";
}

// Getter for the description.
const char* VariableContent::GetDescription() const {
  if (var_type_ == VT_INTEGER) { return iIt->info_str(); }
  else if (var_type_ == VT_BOOLEAN) { return bIt->info_str(); }
  else if (var_type_ == VT_DOUBLE) { return dIt->info_str(); }
  else if (var_type_ == VT_STRING) { return sIt->info_str(); }
  else return NULL;
}

// Getter for the value.
const char* VariableContent::GetValue() const {
char* msg = new char[1024];
  if (var_type_ == VT_INTEGER) {
    sprintf(msg, "%d", ((inT32) *(iIt)));
  } else if (var_type_ == VT_BOOLEAN) {
    sprintf(msg, "%d", ((BOOL8) * (bIt)));
  } else if (var_type_ == VT_DOUBLE) {
    sprintf(msg, "%g", ((double) * (dIt)));
  } else if (var_type_ == VT_STRING) {
    if (((STRING) * (sIt)).string() != NULL) {
      sprintf(msg, "%s", ((STRING) * (sIt)).string());
    } else {
      return "Null";
    }
  }
  return msg;
}

// Setter for the value.
void VariableContent::SetValue(const char* val) {
// TODO (wanke) Test if the values actually are properly converted.
// (Quickly visible impacts?)
  changed_ = TRUE;
  if (var_type_ == VT_INTEGER) {
    iIt->set_value(atoi(val));
  } else if (var_type_ == VT_BOOLEAN) {
    bIt->set_value(atoi(val));
  } else if (var_type_ == VT_DOUBLE) {
    dIt->set_value(strtod(val, NULL));
  } else if (var_type_ == VT_STRING) {
    sIt->set_value(val);
  }
}

// Gets the up to the first 3 prefixes from s (split by _).
// For example, tesseract_foo_bar will be split into tesseract,foo and bar.
void VariablesEditor::GetPrefixes(const char* s, STRING* level_one,
                                                 STRING* level_two,
                                                 STRING* level_three) {
  char* p = new char[1024];
  GetFirstWords(s, 1, p);
  *level_one = p;
  GetFirstWords(s, 2, p);
  *level_two = p;
  GetFirstWords(s, 3, p);
  *level_three = p;
  delete[] p;
}

// Compare two VC objects by their name.
int VariableContent::Compare(const void* v1, const void* v2) {
  const VariableContent* one = *reinterpret_cast<const VariableContent* const *>(v1);
  const VariableContent* two = *reinterpret_cast<const VariableContent* const *>(v2);
  return strcmp(one->GetName(), two->GetName());
}

// Find all editable variables used within tesseract and create a
// SVMenuNode tree from it.
// TODO (wanke): This is actually sort of hackish.
SVMenuNode* VariablesEditor::BuildListOfAllLeaves() {  // find all variables.
  SVMenuNode* mr = new SVMenuNode();
  VariableContent_LIST vclist;
  VariableContent_IT vc_it(&vclist);
  std::map<const char*, int> amount;  //to count the # of entries for a specifc char*.

  INT_VARIABLE_C_IT int_it(INT_VARIABLE::get_head());
  BOOL_VARIABLE_C_IT bool_it(BOOL_VARIABLE::get_head());
  STRING_VARIABLE_C_IT str_it(STRING_VARIABLE::get_head());
  double_VARIABLE_C_IT dbl_it(double_VARIABLE::get_head());

  // Add all variables to a list.
  for (int_it.mark_cycle_pt(); !int_it.cycled_list(); int_it.forward()) {
    vc_it.add_after_then_move(new VariableContent(int_it.data()));
  }

  for (bool_it.mark_cycle_pt(); !bool_it.cycled_list(); bool_it.forward()) {
    vc_it.add_after_then_move(new VariableContent(bool_it.data()));
  }

  for (str_it.mark_cycle_pt(); !str_it.cycled_list(); str_it.forward()) {
    vc_it.add_after_then_move(new VariableContent(str_it.data()));
  }

  for (dbl_it.mark_cycle_pt(); !dbl_it.cycled_list(); dbl_it.forward()) {
    vc_it.add_after_then_move(new VariableContent(dbl_it.data()));
  }

  // Count the # of entries starting with a specific prefix.
  for (vc_it.mark_cycle_pt(); !vc_it.cycled_list(); vc_it.forward()) {
    VariableContent* vc = vc_it.data();
    STRING tag;
    STRING tag2;
    STRING tag3;

    GetPrefixes(vc->GetName(), &tag, &tag2, &tag3);
    amount[tag.string()]++;
    amount[tag2.string()]++;
    amount[tag3.string()]++;
  }

  vclist.sort(VariableContent::Compare);  // Sort the list alphabetically.

  SVMenuNode* other = mr->AddChild("OTHER");

  // go through the list again and this time create the menu structure.
  vc_it.move_to_first();
  for (vc_it.mark_cycle_pt(); !vc_it.cycled_list(); vc_it.forward()) {
    VariableContent* vc = vc_it.data();
    STRING tag;
    STRING tag2;
    STRING tag3;
    GetPrefixes(vc->GetName(), &tag, &tag2, &tag3);

    if (amount[tag.string()] == 1) { other->AddChild(vc->GetName(), vc->GetId(),
                                            vc->GetValue(),
                                            vc->GetDescription());
    } else {  // More than one would use this submenu -> create submenu.
      SVMenuNode* sv = mr->AddChild(tag.string());
      if ((amount[tag.string()] <= MAX_ITEMS_IN_SUBMENU) || (amount[tag2.string()] <= 1)) {
        sv->AddChild(vc->GetName(), vc->GetId(),
                     vc->GetValue(), vc->GetDescription());
      } else {  // Make subsubmenus.
        SVMenuNode* sv2 = sv->AddChild(tag2.string());
        sv2->AddChild(vc->GetName(), vc->GetId(),
                      vc->GetValue(), vc->GetDescription());
      }
    }
  }
  return mr;
}

// Event listener. Waits for SVET_POPUP events and processes them.
void VariablesEditor::Notify(const SVEvent* sve) {
  if (sve->type == SVET_POPUP) {  // only catch SVET_POPUP!
    char* param = sve->parameter;
    if (sve->command_id == writeCommands[0]) {
      WriteVars(param, false);
    } else if (sve->command_id == writeCommands[1]) {
      WriteVars(param, true);
    } else {
      VariableContent* vc = VariableContent::GetVariableContentById(
          sve->command_id);
      vc->SetValue(param);
      sv_window_->AddMessage("Setting %s to %s",
                             vc->GetName(), vc->GetValue());
    }
  }
}

// Integrate the variables editor as popupmenu into the existing scrollview
// window (usually the pg editor). If sv == null, create a new empty
// empty window and attach the variables editor to that window (ugly).
VariablesEditor::VariablesEditor(ScrollView* sv) {
  if (sv == NULL) {
    const char* name = "VarEditorMAIN";
    sv = new ScrollView(name, 1, 1, 200, 200, 300, 200);
  }

  sv_window_ = sv;

  //Only one event handler per window.
  //sv->AddEventHandler((SVEventHandler*) this);

  SVMenuNode* svMenuRoot = BuildListOfAllLeaves();

  STRING varfile;
  varfile = datadir;
  varfile += VARDIR;             // variables dir
  varfile += "edited";           // actual name

  SVMenuNode* std_menu = svMenuRoot->AddChild ("Build Config File");

  writeCommands[0] = nrVariables+1;
  std_menu->AddChild("All Variables", writeCommands[0],
                     varfile.string(), "Config file name?");

  writeCommands[1] = nrVariables+2;
  std_menu->AddChild ("changed_ Variables Only", writeCommands[1],
                      varfile.string(), "Config file name?");

  svMenuRoot->BuildMenu(sv, false);
}


// Write all (changed_) variables to a config file.
void VariablesEditor::WriteVars(char *filename,    // in this file
                                bool changes_only  // changed_ vars only?
                               ) {
  FILE *fp;                      // input file
  char msg_str[255];
                                 // if file exists
  if ((fp = fopen (filename, "r")) != NULL) {
    fclose(fp);
    sprintf (msg_str, "Overwrite file " "%s" "? (Y/N)", filename);
    int a = sv_window_->ShowYesNoDialog(msg_str);
    if (a == 'n') { return; }  // dont write
  }


  fp = fopen (filename, "w");  // can we write to it?
  if (fp == NULL) {
    sv_window_->AddMessage("Cant write to file " "%s" "", filename);
    return;
  }

  for (std::map<int, VariableContent*>::iterator iter = vcMap.begin();
                                          iter != vcMap.end();
                                          ++iter) {
    VariableContent* cur = iter->second;
    if (!changes_only || cur->HasChanged()) {
      fprintf (fp, "%-25s   %-12s   # %s\n",
               cur->GetName(), cur->GetValue(), cur->GetDescription());
    }
  }
  fclose(fp);
}
#endif
