/*
 * Copyright (c) 2009, Takeyuki Nagao
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or
 * without modification, are permitted provided that the
 * following conditions are met:
 * 
 *  * Redistributions of source code must retain the above
 *    copyright notice, this list of conditions and the
 *    following disclaimer.
 *  * Redistributions in binary form must reproduce the above
 *    copyright notice, this list of conditions and the
 *    following disclaimer in the documentation and/or other
 *    materials provided with the distribution.
 *    
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
 * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
 * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 */

package jp.sourceforge.dvibrowser.dvicore.special;

import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import jp.sourceforge.dvibrowser.dvicore.DviException;
import jp.sourceforge.dvibrowser.dvicore.DviObject;
import jp.sourceforge.dvibrowser.dvicore.api.DviContextSupport;
import jp.sourceforge.dvibrowser.dvicore.api.DviDocument;


// TODO: support Logging
public class HtmlSpecialParser
extends DviObject
{
  private final AnchorSet anchorSet = new AnchorSet();
  private final PrivateExecuter exe;

  public HtmlSpecialParser(DviContextSupport dcs)
  {
    super(dcs);
    exe = new PrivateExecuter(dcs, anchorSet);
  }

  private static final Pattern hrefPat
    = Pattern.compile(
      "\\s*html:\\s*<a\\s*href\\s*=\\s*\"(.*)\"\\s*>",
      Pattern.CASE_INSENSITIVE
    );
  private static final Pattern namePat
    = Pattern.compile(
      "\\s*html:\\s*<a\\s*name\\s*=\\s*\"(.*)\"\\s*>",
      Pattern.CASE_INSENSITIVE
    );
  private static final Pattern endPat
    = Pattern.compile(
      "\\s*html:\\s*</a>",
      Pattern.CASE_INSENSITIVE
    );
  
  public void execute(DviDocument doc)
  throws DviException
  {
    for (int p=0; p<doc.getTotalPages(); p++) {
      exe.execute(doc.getPage(p));
    }
  }

  public AnchorSet getAnchorSet()
  {
    return anchorSet;
  }

  private static class PrivateExecuter
  extends AbstractDviSpecialExecutor
  {
    private final AnchorSet anchorSet;
    public PrivateExecuter(DviContextSupport dcs, AnchorSet anchorSet) {
      super(dcs);
      this.anchorSet = anchorSet;
    }

    private static class StackItem
    {
      private final long start;
      private final String tag;
      private final String data;
      public StackItem(long start, String tag, String data) {
        this.start = start;
        this.tag = tag;
        this.data = data;
      }
    }

    private final Stack<StackItem> stack
      = new Stack<StackItem>();

    public void doSpecial(byte [] _xxx)
    throws DviException
    {
      String xxx = new String(_xxx);
      Matcher mat;
      DviExecutorContextImpl ctx = getExecutorContext();
      if ((mat = hrefPat.matcher(xxx)).matches()) {
        stack.push(
          new StackItem(
            ctx.getCommandRange().begin(),
            "href",
            mat.group(1)
          )
        );
      } else if ((mat = namePat.matcher(xxx)).matches()) {
        stack.push(
          new StackItem(
            ctx.getCommandRange().begin(),
            "name",
            mat.group(1)
          )
        );
      } else if ((mat = endPat.matcher(xxx)).matches()) {
        Anchor el = null;
        if (!stack.empty()) {
          StackItem it = stack.pop();
          if ("href".equals(it.tag)) {
            el = new Anchor.Href(
              it.start,
              ctx.getCommandRange().end(),
              it.data
            );
          } else if ("name".equals(it.tag)) {
            el = new Anchor.Name(
              it.start,
              ctx.getCommandRange().end(),
              it.data
            );
          }
        } else {
          // TODO: logging
//          System.out.println("html tag stack underflow.");
          // This is an error.  We ignore it.
        }
        if (el != null) {
          anchorSet.add(el);
        }
      }
    }
  }
}
