/*
* IDOS command for Ubiquity
* version 1.16
*
* changes since last release:
* - made compatible with both Parser1 and Parser2
*/


/* ***** BEGIN LICENSE BLOCK *****
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (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.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is IDOS command.
*
* The Initial Developer of the Original Code is Wegguy.
* Portions created by Initial Developer are Copyright (C) 2008-9
* Initial Developer. All Rights Reserved.
* ***** END LICENSE BLOCK ***** */



/**
* Searches IDOS.cz
*
* Syntax: idos from <from> to <to> using <bus|train|all|...> on <date and time>
*
* For the "using" attribute "all" is default value.
*
* If date and time are missing, a value derived from current time is used -
* by default 5 minutes into the future, however this can be changed through
* setting a new variable in about:config named
* extensions.ubiquity.commanddata.idos.defaulttimeaddminutes
*
* If <from> or <to> isn't filled, a form with all other data filled is
* displayed instead of search results page.
*/





/**
* Make suggestions based on input
*
* @param inputText user input
* @param suggestedValues possible suggestions
* @param disallowedSubstrings values we don't like
* @param strictEnum does the suggestedValues act as complete enumeration of possible values?
*
* @return array of suggestions
*/
function suggest(inputText, suggestedValues, disallowedSubstrings, strictEnum) {
  var results = [];
 
    
  /* first check the input doesn't contain disallowed*/
  for (i in disallowedSubstrings) {
    if (inputText.search(disallowedSubstrings[i]) > -1) {
      return [];
    }
  }

  /* suggest matching values from suggestedValues array */
  for (var i in suggestedValues) {
    if (suggestedValues[i].indexOf(inputText) == 0) {
      results.push(CmdUtils.makeSugg(suggestedValues[i], null, null));
    }
  }

  /* if enum isn't strict, suggest the text user wrote as well */
  if (!strictEnum) {
    results.push(CmdUtils.makeSugg(inputText, null, null));
  }

  return results;
}

/* TODO dynamic list would be nice */
/** some basic city suggestions */
var cities = ["praha", "brno", "ostrava", "plzen", "olomouc", "liberec", "ceske budejovice", "hradec kralove", "usti nad labem", "pardubice", "karlovy vary", "jihlava", "zlin"];

/* TODO fill dynamicaly */
/** Timetable list */
var transportTypeStrings = {
  vse : "vlakyautobusymhd",
  all : "vlakyautobusymhd",
  vlakyautobusy : "vlakyautobusy",
  
  vlak : "vlaky",
  train : "vlaky",
  bus : "autobusy",
  letadlo : "letadla",
  plane : "letadla",
  
  pid : "pid",
  idsjmk : "idsjmk",
  jmk : "idsjmk",
  
  mhdpraha : "praha",
  mhdbrno : "brno",
  mhdostrava : "ostrava",
  mhdplzen : "plzen",
  mhdolomouc : "olomouc",
  mhdliberec : "liberec",
  mhdceskebudejovice : "ceskebudejovice",
  mhdhradeckralove : "hradeckralove",
  mhdusti : "ustinadlabem",
  mhdpardubice : "pardubice",
  mhdkarlovyvary : "karlovyvary",
  mhdjihlava : "jihlava",
  mhdzlin : "zlin",
  
  
  mhdas : "as",
  mhdbenesov : "benesov",
  mhdberoun : "beroun",
  mhdblansko : "blansko",
  mhdbruntal : "bruntal",
  mhdbreclav : "breclav",
  mhdcaslav : "caslav",
  mhdceskalipa : "ceskalipa",
  mhdceskytesin : "ceskytesin",
  mhddecin : "decin",
  mhddomazlice : "domazlice",
  mhddvurkralove : "dvurkralove",
  mhdfrydekmistek : "frydekmistek",
  mhdhavirov : "havirov",
  mhdhavlickuvbrod : "havlickuvbrod",
  mhdhodonin : "hodonin",
  mhdcheb : "cheb",
  mhdchomutov : "chomutov",
  mhdchrudim : "chrudim",
  mhdjablonec : "jablonec",
  mhdjachymov : "jachymov",
  mhdjindrichuvhradec : "jindrichuvhradec",
  mhdkarvina : "karvina",
  mhdkladno : "kladno",
  mhdklatovy : "klatovy",
  mhdkolin : "kolin",
  mhdkrnov : "krnov",
  mhdkromeriz : "kromeriz",
  mhdkutnahora : "kutnahora",
  mhdmelnik : "melnik",
  mhdmladaboleslav : "mladaboleslav",
  mhdnymburk : "nymburk",
  mhdorlova : "orlova",
  mhdpelhrimov : "pelhrimov",
  mhdpisek : "pisek",
  mhdpolicka : "policka",
  mhdprostejov : "prostejov",
  mhdprerov : "prerov",
  mhdpribram : "pribram",
  mhdsokolov : "sokolov",
  mhdstrakonice : "strakonice",
  mhdstribro : "stribro",
  mhdstudenka : "studenka",
  mhdsumperk : "sumperk",
  mhdtabor : "tabor",
  mhdtachov : "tachov",
  mhdtrutnov : "trutnov",
  mhdtrinec : "trinec",
  mhdvalasskemezirici : "valasskemezirici",
  mhdvelkemezirici : "velkemezirici",
  mhdvlasim : "vlasim",
  mhdvsetin : "vsetin",
  mhdvyskov : "vyskov",
  mhdzabreh : "zabreh",
  mhdznojmo : "znojmo",
  mhdzdarnadsazavou : "zdarnadsazavou"
};




/** source and destination noun type */
var noun_place = {
  _name: "place",
  suggest: function( inText, html ) {
    if (typeof inText != "string") {
      return [];
    }

    var additionalPlacesString = Application.prefs.getValue(
          "extensions.ubiquity.commanddata.idos.additionalplaceslist", "");
          
    var myPlaces = additionalPlacesString.split(",").concat(cities);

    var badWords = [/^(.* )?to( .*)?$/i, /^(.* )?from( .*)?$/i, /^(.* )?by( .*)?$/i,
      /^(.* )?on( .*)?$/i, /^(.* )?with( .*)?$/i,
      /1/, /2/, /3/, /4/, /5/, /6/, /7/, /8/, /9/, /0/];

    return suggest(inText, myPlaces, badWords, false);
  }
};


/** datetime noun type */
var noun_type_date_time = {
  _name: "date",
  suggest: function( text, html )  {
    if (typeof text != "string") {
      return [];
    }

    if (text == "") {
      /* if input is blank, suggest now */
      return this.suggest("now");
    }

    var date = Date.parse(text);
    if (!date) {
      return [];
    }
    text = date.toString("dd.MM.yyyy H:mm");
    return [ CmdUtils.makeSugg(text, null, date) ];
  }
};

/** type of transport noun type */
var noun_type_transport = {
  _name: "transport type",
  suggest: function( inText, inHtml )  {
    if (typeof inText != "string") {
      return [];
    }


    if (inText == "") {
      /* if input is blank, suggest all */
      return this.suggest(
        Application.prefs.getValue(
          "extensions.ubiquity.commanddata.idos.defaulttransporttype", "all"),
        null);
    }


    var suggestions = Array();
    for (ttype in transportTypeStrings) {
      suggestions.push(ttype);
    }

    return suggest(inText, suggestions, [], true);
  }
};



/* The IDOS command */
var cmdObj = {
  icon: "http://jizdnirady.idnes.cz/idos.ico",
  homepage: "http://ubiquity.wegguy.cz/idos",
  author: { name: "Wegguy", homepage: "http://www.wegguy.cz"},
  contributors: ["Vaclav Synacek"],
  description: "Searches IDOS.cz",
  help: "Searches IDOS.cz - Czech Republic's public transport information and connections finder."
        + " Takes departure and destination, date and means of transport.",
  license: "MPL",

  /* command name and arguments defined below in parser version specific section */
  
  _urlTemplate: 'http://jizdnirady.idnes.cz/${by}/spojeni/',
  _dataTemplate: '?f=${from}&t=${to}&date=${date}&time=${time}&submit=${autostart}&pt=true',
  

  /**
   * Parses commands input
   *
   * @return array [from, to, by, date, time]
   */
  _parseInput: function(from, mods) {
    var addMinutes = Application.prefs.getValue(
              "extensions.ubiquity.commanddata.idos.defaulttimeaddminutes", 5);
    var in5 = new Date(new Date( Date.now() ).getTime() + addMinutes * 60 * 1000);
    var defaults = {
      from : Application.prefs.getValue(
          "extensions.ubiquity.commanddata.idos.defaultorigin", ""),
      to : Application.prefs.getValue(
          "extensions.ubiquity.commanddata.idos.defaultdestination", ""),
      by : Application.prefs.getValue(
          "extensions.ubiquity.commanddata.idos.defaulttransporttype", "all"),
      on: in5.toString("dd.MM.yyyy H:mm")
    };
 
    var fromText = (from.text) ? from.text : defaults.from;
    var toText = (mods.to.text) ? mods.to.text : defaults.to;
    var byText = (mods.by.text) ? mods.by.text : defaults.by;
    var onText = (mods.on.text) ? mods.on.text : defaults.on;
    
    var tmpDatetime = onText.split(" ", 2);
    var onDate = tmpDatetime[0];
    var onTime = tmpDatetime[1];
    
    result = {
      from: fromText,
      to: toText,
      by: transportTypeStrings[byText],
      date: onDate,
      time: onTime
    };
    
    return result;
  },
  
  /**
   * Encodes subs to URI friendly format
   *
   * @return array [from, to, by, date, time]
   */
  _encodeParams: function(subs) {
    
    var result = {};
    
    // add any untranslated
    for (it in subs) {
      result[it] = encodeURIComponent(subs[it]);
    }
   
    return result;
  },
  
  
  
  _makePreview: function (previewUrl, previewJson, callback) {
    
    jQuery.ajax({
      type: "GET",
      url: previewUrl,
      data: previewJson,
      dataType: "html",
      error: function() {
        //displayMessage("Preview error - page couldn't be loaded.");
      },
      success: function(xml) {
        CmdUtils.loadJQuery( function(jQuery) {
          var results = jQuery(xml).find(".datarow.first");
          
          results.find('td').css('padding-right','1em');

          var result = "";
          if (results.length == 0) {
            /* Try fetch error message */
            result = jQuery(xml).find("strong.red").html();
          } else {
            /* concat results */
            var maxresults = Math.min(results.length, Application.prefs.getValue(
              "extensions.ubiquity.commanddata.idos.maxpreviewresults", "4"));
             
            for (i = 0; i < maxresults; i++) {
              results.eq(i).find("td.note").remove();
              results.eq(i).find("td:eq(0), td:eq(3), td:eq(5), img").remove();
              
              /* append destination and end time */
              var lastrow = results.eq(i).parent().find(".datarow:last");
              lastrow.find("td:eq(0), td:eq(1), td:eq(4), td:eq(5), td:eq(6), img").remove();
              lastrow.find('td').css('padding-right','1em');
              
              result += "<tr>" + results[i].innerHTML + lastrow.html() + "</tr>";
            }
          }
          
          callback(result);
        });
      }
    });
  
  },
  
  _previewBase: function(pblock, from, mods) {
    var subs = this._parseInput(from, mods);
   
    var loading = false;
    
    if (subs.from && subs.to) {
      var previewUrl = CmdUtils.renderTemplate(this._urlTemplate, this._encodeParams(subs));
      var previewJson = {
        f: subs.from,
        t: subs.to,
        date: subs.date,
        time: subs.time,
        submit: "true"
      };
      
      loading = true;
      this._makePreview(previewUrl, previewJson, function(text) {
        if (text && (text.indexOf("<td") > -1)) {
          text = text.replace(/<a href="[^"]*"/g, "<span").replace(/<\/a>/g, "</span>");
          pblock.innerHTML = '<div id="idos"><small><table><tr>' + text
            + '</tr></table></small></div>';
        } else {
          pblock.innerHTML = ((text) ? text : _('[nothing found]'));
        }
      });
      
    }
    
    /* only use default preview if no dynamic preview is in place */
    if (pblock.innerHTML.indexOf('<div id="idos">') == -1) {  
      var msg = _('<div>Searches for${by} connections${from}${to}${on}.</div>');
      var subs2 = {
        from: (subs.from) ? ' from <strong>' + subs.from + '</strong>' : '',
        to: (subs.to) ? ' to <strong>' + subs.to + '</strong>' : '',
        by: ' <strong>' + subs.by + '</strong>',
        on: ' on <strong>' + subs.date + ' ' + subs.time + '</strong>'
      };      
          
      pblock.innerHTML = CmdUtils.renderTemplate(msg, subs2);
    }
    
    
    if (loading) {
      pblock.innerHTML = "<div style='float: right'>"
        + "<img src='chrome://browser/skin/Throbber.gif' alt='loading...' /></div>"
        + pblock.innerHTML;
    }

    
  },
  

  
  _executeBase: function(from, mods) {
    
    subs = this._parseInput(from, mods);

    if (!subs.from || !subs.to) {
      /* display IDOS homepage anyways, with all other fields filled */
      subs.autostart = "false";
    } else {
      subs.autostart = "true";
    }
    
    Utils.openUrlInBrowser(CmdUtils.renderTemplate(
      this._urlTemplate + this._dataTemplate, this._encodeParams(subs)   
    ));    
  }
};



/* Parser version dependent code */
if (CmdUtils.parserVersion == 2) {
  /* Parser 2: */
  cmdObj.names = ["idos", "find connection", "find connections"];
  cmdObj.arguments = [
    {role: 'source', nountype: noun_place, label: 'source'},
    {role: 'goal', nountype: noun_place, label: 'destination'},
    {role: 'time', nountype: noun_type_date_time, label: 'time'},
    {role: 'instrument', nountype: noun_type_transport, label: 'means of transport'}
  ],
  
  cmdObj.preview = function(pblock, args) {
    cmdObj._previewBase(pblock, args.source,
      {
        to: args.goal,
        by: args.instrument,
        on: args.time
      }
    );
  };
  
  cmdObj.execute = function(args) {
    cmdObj._executeBase(args.source,
      {
        to: args.goal,
        by: args.instrument,
        on: args.time
      }
    );
  };
}
else {
  /* Parser 1: */
  cmdObj.name = "idos";
  cmdObj.takes = {"from": noun_place};
  cmdObj.modifiers = {
    to: noun_place,
    by: noun_type_transport,
    on: noun_type_date_time
  };
  
  // backwards-compatibility for the i18n support
  if (_ == null) {
    _ = function(string) {return string;};
  }
  
  cmdObj.preview = cmdObj._previewBase;
  cmdObj.execute = cmdObj._executeBase;
}

CmdUtils.CreateCommand(cmdObj);

