/************************************

  "waka" is a Japanese poetry 
         composed of 31 syllables. 

  (c) 2005 Pixel-Apes.

  -----------------------------------

  Version 0.95

  Waka31 JavaScript wiki formatter.
  It formats plain text with wiki-markup into HTML.

  See README.TXT and LICENSE.TXT for further details

*************************************/


function wakaFormatter() { this.init(); }

wakaFormatter.prototype = 
{
    // css classes modifiers, see waka.css
    cssPrefix  : "wiki-",
    cssPostfix : "",

    // available presets
    defaultPreset : [ "links", "skipIgnored", "default", "returnIgnored" ],
    presets : {
        "todo"    : { // todo markup
                      list:  [ wakaTodo ],
                      empty: wakaTodoContent,
                      next:  [ "links", "skipIgnored", "default", "returnIgnored" ] 
                      // same as defaultPreset
                    },
        "sections": { // Useful
                      list:  [ wakaSections ],
                      empty: wakaSectionsContent,
                      next:  [ "links", "skipIgnored", "default", "returnIgnored" ]
                      // same as defaultPreset
                    },
        "table"   : { // Intrinsic (do not use at all)
                      list: [ wakaTablesRow ],
                      empty: wakaEmpty,
                      next: "default"
                    },
        "links"   : {
                      list: [ wakaLinks, wakaIgnore ],
                      empty: wakaToken,
                      next : false
                    },  
        "default" : { // Main cycle
                      list: [ wakaDecoration, wakaDecorationWord, wakaDecorationGlue, wakaDecorationHeaders,
                              wakaLists, wakaEmailQuotes, wakaTables ],
                      empty: wakaToken,
                      next: "symbols"
                    },
        "symbols" : { // Very simple, but sometimes useful one
                      list: [ wakaSymbols ],
                      empty: wakaToken,
                      next: false
                    },
        "skipIgnored"    : { isSystem : true },
        "returnIgnored"  : { isSystem : true }

              },

    // stuff for ignoring
    ignoreRE      : /<waka31ignore>((.|\n)*?)<\/waka31ignore>/,
    ignoreTagName : "waka31ignore",
    ignoreChar    : "\xA2",
                  // \xA3 is also used below.

    // prebuild regexp for all presets
    init : function()
    {
      for( var i in this.presets )
      {
        var strings = [];
        var c=0;
        for (var j in this.presets[i].list)
          strings[c++] = this.presets[i].list[j].prototype.getRegexpPart();
        this.presets[i].re = new RegExp( strings.join("|"), "" );
      }
    },

    // returns all matches of "re" in a given "what".
    // close analogue to PHP's "preg_replace_callback"
    _allMatchesSpecialChar : "\xA3", // used to avoid treating end-of-match as start-of-text for next match
    _allMatches : function( re, what )
    {
      var matches=[];
      var m = true;
      var c=0;
      matches[c-1] = { "match" : "!fake!",
                       "start" : 0,
                       "end"   : 0,
                       "fake"  : true
                     };
      while( m )
      {
        if (c == 0)
          m = what.match(re);
        else
          m = (this._allMatchesSpecialChar + what).match(re);
        if (m)
        {
          var pos = what.indexOf(m[0]);

          if (pos > 0)
          {
            matches[c] = { "match" : what.substr( 0, pos),
                           "start" : matches[c-1]["end"],
                           "end"   : matches[c-1]["end"]+pos,
                           "plain" : true
                         };
            c++;
          }
          

            matches[c] = { "match" : m[0], 
                           "start" : matches[c-1]["end"]+pos, 
                           "end"   : matches[c-1]["end"]+pos+m[0].length
                         };
          c++;                          
          what = what.substr( pos+m[0].length );
        }
      }
      if (what != "")
            matches[c] = { "match" : what,
                           "start" : matches[c-1]["end"],
                           "end"   : matches[c-1]["end"]+what.length,
                           "plain" : true
                         };

      return matches;
    },


    // build tree of tokens for further compilation
    // could be build, then analized.
    buildTree : function( what, preset )
    {
      what = String(what).replace( /\r/g, "" );
      if (!this.presets[preset]) preset = this.defaultPreset;

      // 1. get all matches.
      var matches = this._allMatches( this.presets[preset].re, what );

      // 2. step by step check`n`build 
      var tree = []; 
      var token = false;
      var tokenList = [];
      var c=0;
      for( var i in matches )
      if (!matches[i]["fake"])
      {
        if (matches[i]["plain"]) 
        {
          token = new this.presets[preset].empty();
          token.bind( this );
          token.setNextPreset( this.presets[preset].next );
          tokenList = token.build( matches[i]["match"], tree[c-1] );
          for (var k=0; k<tokenList.length; k++)
            tree[ c++ ] = tokenList[k];
        }
        else
          for( var j in this.presets[preset].list )
          {
            var struct = this.presets[preset].list[j].prototype.is( matches[i]["match"] );
            if (struct)
            {
              token = new this.presets[preset].list[j]();
              token.bind( this );
              token.setNextPreset( this.presets[preset].next );
               tokenList = token.build( struct, tree[c-1] );
              for (var k=0; k<tokenList.length; k++)
                tree[ c++ ] = tokenList[k];
              break;
            }
          }
      }
      return tree.slice(0, c);
    },

    // MAIN METHOD. USE THIS ONE.
    format : function( what, preset ) // or presetArray
    {
      if (!this.presets[preset]) preset = this.defaultPreset;

      var ignored, arr;
      if (typeof preset == "object")
      {
        for (var i in preset)
        if (preset[i] == "skipIgnored")
        {
          ignored = [];
          while ((arr = what.match( this.ignoreRE )) != null)
          {
            arr.index     = what.indexOf( arr[0] );
            arr.lastIndex = arr.index + arr[0].length;
            ignored = ignored.concat( arr[1] );
            what = what.substr( 0, arr.index ) + 
                   this.ignoreChar +
                   what.substr( arr.lastIndex );
          }
        }
        else
        if (preset[i] == "returnIgnored")
        {
          var _what = what.split( this.ignoreChar );
          what = "";
          for (var i=0; i<_what.length; i++)
          {
            what += _what[i];
            if (ignored[i]) what+=ignored[i];
          }
        }
        else
          what = this.format( what, preset[i] );
        
        return what;
      }

      var tree = this.buildTree( what, preset );
      return this.formatTree( tree );
    },


    // formats prebuilt tree to HTML
    // useful, because you could then modify tree and just recompile it to HTML
    formatTree : function( tree )
    {
      var result = "";
      var last = false;  
      for( var i=0; i<tree.length; i++ )
      {
        result += tree[i].toHtml();
        last = tree[i];
      }
      if (last) result+= last.separateFromNext( false );
      return result;
    },



    // simple function to end the class definition. I really like this one.
    undef : function( param ) { return param; }
}





