//
// C++ Implementation: lobject
//
// Description:
//
//
// Author: Sheldon Lee Wen <tormak@rogers.com>, (C) 2004
//
// Copyright: See COPYING file that comes with this distribution
//
//
#include <iostream>
#include <algorithm>
#include "lineak/lobject.h"
#include "lineak/lcommand.h"
#include "lineak/definitions.h"
#include <lineak/lineak_core_functions.h>

using namespace std;
using namespace lineak_core_functions;

LObject::LObject()
{
        name = snull;
        toggle = false;
        dtoggle = false;
        //modifier = 0;
        event_type = PRESS;
        type  = CODE;
	modifiers.clear();
	//modifiers.push_back(0);
	
}
LObject::~LObject()
{
}
/*
bool LObject::operator==(LObject &rhs) {
  if (this != rhs.this ) {
     if (name != rhs.name) return false;
     if (event_type != rhs.event_type) return false;
     if (type != rhs.type) return false;
     if (modifier != rhs.modifier) return false;
     if (toggle != rhs.toggle) return false;
     if (dtoggle != rhs.toggle) return false;
    
     if (commands != rhs.commands) return false;
     if (toggle_modifiers != rhs.toggle_modifiers) return false;
     if (toggle_names != rhs.toggle_names) return false;
  }
  return true;
}
*/
LObject::LObject(string iname, EventType_t ietype, KeyType_t itype) : name(iname), event_type(ietype), type(itype)
{
        toggle = false;
        //internal_init();
}
/** If this is not a toggleable key, return the command that corresponds to the name
        as defined in the keyboard definitions file. Else, return the command for the current
        state of the key and put the current state at the end of the queue. */
LCommand & LObject::getCommand(unsigned int mod) {
        if (toggle) {
	   return tog_commands[toggle_names.front()];
        }
	return commands[mod];
}
string & LObject::getCommandDisplayName(unsigned int mod) {
	if (toggle) {
	   return tog_commandDisplayNames[toggle_names.front()];
	}
	return commandDisplayNames[mod];
}
/** Set the command for a non-togglable key. */
void LObject::setCommand(LCommand command, unsigned int mod) {
        if (!command.isEmpty()) {
           if (!toggle) {
	      commands[mod] = command;
	      if (!hasModifier(mod))
	         addModifier(mod);
	   }
        }
}
/** Set the command display name for a non-toggleable key. */
void LObject::setCommandDisplayName(string dname, unsigned int mod) {
       if (!toggle) {
	  commandDisplayNames[mod] = dname;
       }	  
}
void LObject::setToggleCommandDisplayName(string dname, const string iname) {
      if (toggle && iname != snull) {
	  tog_commandDisplayNames[iname] = dname;
      }
}
/** Set the command for a toggle state. This needs to have a properly parsed name. */
void LObject::setCommand(LCommand command, string iname) {
        if (!command.isEmpty()) {
           if (toggle && (iname != snull) && (iname != "")) {
	      tog_commands[iname] = command;
           } 
        }
}
bool LObject::hasModifier(const unsigned int modifier) {
   for (vector<unsigned int>::size_type i=0; i < modifiers.size(); i++) {
      if ( modifiers[i] == modifier )
        return true;
   }
      return false;
}
string LObject::getModifierString(unsigned int modifier) {
	return lineak_core_functions::getModifierString(modifier);
}
string LObject::getEventTypeString() {
	return lineak_core_functions::getEventTypeString(event_type);        
}
string LObject::getTypeString() {
	return lineak_core_functions::getTypeString(type);
}
void LObject::serialize(ostream &out) {
   int com=0;
   if (name != snull) {
      out << "object=" << name;
      out << ":event_type=" << lineak_core_functions::getEventTypeString(event_type);
      out << ":type=" << lineak_core_functions::getTypeString(type);
      if (!isToggle()) {
          out << ":toggle=false";
          for (map<unsigned int,LCommand>::iterator i = commands.begin(); i!= commands.end(); i++, com++) {
	     out << ":command_entry=" << com;
             out << ",modifier=" << lineak_core_functions::getModifierString((i->first));
             out << ",command=" << commands[(i->first)];
             out << ",display_name=" << commandDisplayNames[(i->first)];
          }
       }
       else {
          out << ":toggle=true" << endl;
          string s;
          for (queue<string>::size_type i = 0; i<toggle_names.size(); i++, com++) {
             s = toggle_names.front();
	     out << ":command_entry=" << com;
             out << ",toggle_name=" << s;
             out << ",command=" << tog_commands[s];
             out << ",display_name=" << tog_commandDisplayNames[s];
             toggle_names.pop();
             toggle_names.push(s);
          }
      }
  }
  else {
    error("Attempting to output an empty key!");
  }
}

void LObject::print(ostream&out) {
   if (name != snull) {
      out << "Object: " << name << endl;
      out << "   event_type = " << lineak_core_functions::getEventTypeString(event_type) << endl;
      out << "   type = " << lineak_core_functions::getTypeString(type) << endl;
      if (!isToggle()) {
         out << " toggle = false" << endl;
         for (map<unsigned int,LCommand>::iterator i = commands.begin(); i!= commands.end(); i++) {
            out << "   modifier = " << lineak_core_functions::getModifierString((i->first));
            out << " and command = " << commands[(i->first)] << endl;
	    out << " and display name = " << commandDisplayNames[(i->first)] << endl;
         }
      }
      else {
         out << " toggle = true" << endl;
         out << " number of toggle names = " << toggle_names.size() << endl;
         string s;
         for (queue<string>::size_type i = 0; i<toggle_names.size(); i++) {
            s = toggle_names.front();
            out << " toggle name = " << s << endl;
            out << "    and command = " << tog_commands[s] << endl;
	    out << "    and display name = " << tog_commandDisplayNames[s] << endl;
            toggle_names.pop();
            toggle_names.push(s);
         }
      }
   }
   else
      error("Attempting to output an empty key!");
}
void LObject::addToggleName(const string iname) {
        setToggle(true);
	if ( iname != snull ) {
	   toggle_names.push(iname);	   
	} else {
	   string err = "Attempted to add an empty toggle name: " + iname + " to object: " + name;
	   error(err);
	}

}
vector<string> LObject::getToggleNames() {
	vector<string> names;
	queue<string> tmp = toggle_names;
	while(!tmp.empty()) {
	   names.push_back(tmp.front());
	   tmp.pop();
	}
	return names;
}
bool LObject::ownsName(string iname) {
   if ( name == iname )
      return true;

   queue<string> tmp = toggle_names;
   while(!tmp.empty()) {
      if ( iname == tmp.front()  )
         return true;
      tmp.pop();
   }
   return false;
}
// void LObject::setToggleModifier(string iname, unsigned int imodifier) {
//    if (toggle && iname != snull && iname != "") {
//       toggle_modifiers[iname] = imodifier;
//    }
// }
bool LObject::isToggle() const {
        return toggle;
}
void LObject::toggleState() {
        if (toggle) {
           string name = toggle_names.front();
           toggle_names.pop();
           toggle_names.push(name);
        }
}
/** Return the next name in the toggle queue. The toggle is not rotated until
    you actually get the command with getCommand() This is to keep the name
    and the command to be run in sync. */
string LObject::getNextToggleName() {
        if (toggle) {
           return toggle_names.front();
        } else
           return name;
}
void LObject::clear() {
        name = snull;
        toggle = false;
        dtoggle = false;
        //modifier = 0;
        event_type = PRESS;
        type  = CODE;
	modifiers.clear();
	commands.clear();
	commandDisplayNames.clear();
	tog_commands.clear();
	tog_commandDisplayNames.clear();
	//toggle_names.clear();
	//modifiers.push_back(0);

}

ostream & operator<<(ostream &out, LObject &rhs) {
        //rhs.serialize(out);
        rhs.print(out);
        return out;
}


// void LObject::internal_init() {
//     /** Handle keys with names of the form:
//                 name1|name2|name3 */
//     string key = name;
//     int index;
//     while (((unsigned int)(index = key.find('|'))) != string::npos) {
//                 toggle = true;
//                 string tmp = key.substr(0,index);
//                 names.push_back(tmp);
//                 toggle_names.push(tmp);
//                 key.erase(0, index+1);
//      }
//         names.push_back(key);
//         toggle_names.push(key);
// }

