/*
 * Copyright (C) by Netcetera AG.
 * All rights reserved.
 *
 * The copyright to the computer program(s) herein is the property of
 * Netcetera AG, Switzerland.  The program(s) may be used and/or copied
 * only with the written permission of Netcetera AG or in accordance
 * with the terms and conditions stipulated in the agreement/contract
 * under which the program(s) have been supplied.
 *
 */

/**
 * Manages the application's configuration options through the 
 * fragment identifier part of the page's URL 
 */
var Config = {
  
  /**
   * Option parsers. Each option parser is an object with the 
   * following properties:
   * - pattern - can be a string or a regex. If it is a string, it will match
   * a name of an option, if it is a regex it will match the value/
   * - toString - a function taking two parameters: name, value. should 
   * convert the value to a string representation.
   * - fromString - a function taking one parameter. Should convert it to an
   * object representation
  */
  OPTION_PARSERS: [
    {
      pattern: "m",
      
      intoString: function(name, value) {
        return value.toString();
      },
      
      fromString: function(stringValue) {
        return String.toBoolean(stringValue);
      }
    },               
    {
      pattern: "ctime",
      
      intoString: function(name, value) {
        return value.toISOString();
      },
      
      fromString: function(stringValue) {
        return Date.parseISO(stringValue);
      }
    },
    {
      pattern: "numDepartures",
      
      intoString: function(name, value) {
        return value.toString();
      },
      
      fromString: function(stringValue) {
        if (parseInt(stringValue) < 5) {
          return 5;
        }
        if (parseInt(stringValue) > 25) {
          return 25;
        }
        return parseInt(stringValue);
      }
    }
  ],
  
  _urlChecker: /.*#(.+)$/,
  _fragmentRe: /^([^;]+)(;(.+))?$/,
  _ctimeRe: /^ctime=(.+)$/,
  _optionFinderRe: /[^;=]+=[^=;]+/g,
  
  /**
   * Checks the key against each pattern and if it's a match it converts the value. 
   */
  _parseOption: function(key, value) {
    var retval = value;
    $.each(this.OPTION_PARSERS, function() {
      if (typeof this.pattern == "string") {
        if (this.pattern == key) {
          retval = this.fromString(value);
        }
      } else if (typeof this.pattern.constructor == RegExp) {
        if (key.match(this.pattern)) {
          retval  = this.fromString(value);
        }
      }
    });
    
    return retval;
  },
  
  _serializeOption: function(key, value) {
    var retval = value.toString();
    $.each(this.OPTION_PARSERS, function() {
      if (typeof this.pattern == "string") {
        if (this.pattern == key) {
          retval = this.intoString(key, value);
        }
      } else if (typeof this.pattern.constructor == RegExp) {
        if (key.match(this.pattern)) {
          retval  = this.intoString(key, value);
        }
      }
    });
    
    return retval;
  },
  
  /**
   * parses the configuration from a URL
   */
  parseConfig: function(url) {
    var fragment, m;
    if (m = url.match(this._urlChecker)) {
      fragment = m[1];
    } else {
      return null;
    }
        
    var stationUrl, opts = {};
    if (m = fragment.match(this._ctimeRe)) {
      stationUrl = undefined;
      var kv = fragment.split('=')
      var val = Config._parseOption(kv[0], kv[1]);
      opts[kv[0]] = val;
    } else if (m = fragment.match(this._fragmentRe)) {
      stationUrl = m[1];
      if (m.length == 4 && m[3] && (m[3].indexOf('=') > 0) && (m[3].indexOf('=') < m[3].length - 1)) {
        $.each(m[3].match(this._optionFinderRe), function() {
          var kv = this.split('=');
          var val = Config._parseOption(kv[0], kv[1]);
          opts[kv[0]] = val;
        });
      }
    }
    
    return $.extend(
      {
        stationUrl: stationUrl,
        opts: opts
      }, 
      this._configMethods);
  },
  
  /**
   * Loads the configuration from the window's URL and return's it
   */
  load: function() {
    return this.parseConfig(window.location.hash);
  },
  
  // methods that are added to the configuration object returned by load and parseConfig
  _configMethods: {
    
    /**
     * Creates the string in the url after the # sign.
     * All the options from opts are joined in one string and 
     * at the end the stationUrl + options string is returned
     */
    toFragment: function() {
      var kv = [];
      $.each(this.opts, function(k, v) {
        var val = Config._serializeOption(k, v);
        kv.push([k, val].join('='));
      });
      return encodeURI(
        '#' + this.stationUrl + (kv.length > 0 ? (";" + kv.join(';')) : "")
      );
    },
    
    /**
     * Extracts just the URL from the browser, without the options,
     * and adds the new options which are part of the new hash string.
     */
    toUrl: function() {
      var u = window.location.href.split("#");
      return [u[0], this.toFragment()].join("#");
    },
    
    /**
     * stores the configuration into the location's hash
     */
    store: function() {
      window.location.hash = this.toFragment();
    },
    
    isValid: function() {
      return this.stationUrl;
    }
  }
}

/**
 * Extend the String with new methods
 */
$.extend(String, {
  /**
   * Converts the string into a corresponding boolean.
   * @param stringValue (String), it it is "true", the method will return boolean true and
   * vise versa. If it is other value than "true" or " false", it will return undefined. 
   */
  toBoolean: function(stringValue) {
    if (stringValue == "true") {
      return true;
    }
    if (stringValue == "false") {
      return false;
    }
    return undefined;
  }
});
