/*
 * 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.util;

import java.io.BufferedReader;
import java.io.EOFException;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.io.Reader;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import jp.sourceforge.dvibrowser.dvicore.DviException;



// TODO: support logging
// TODO: remove this class
@Deprecated
public class TeXMessageParser
{
  public static final String STATE_INITIAL = "INITIAL";

  public void parse(Reader r, Handler handler)
  throws DviException, IOException
  {
    if (handler == null)
      throw new NullPointerException
        ("handler can't be null");

    Env env = new Env();

//    env.parser  = this;
    env.reader  = new BufferedReader(r);
    env.handler = handler;
    env.state   = STATE_INITIAL;
    env.stop    = false;

    env.firstLine = env.reader.readLine();
    if (env.firstLine == null)
      throw new EOFException
        ("input stream ended while reading the first line.");
    env.handler.begin(env);

    while (!env.stop) {
      String line = env.reader.readLine();
      parseLine(env, line);
    }
  }

  protected void parseLine(Env env, String line)
  throws DviException, IOException
  {
    if (line == null) {
      if (!env.stop) {
        env.handler.end(env);
      }
      env.stop = true;
      return;
    }

    env.handler.onEachLine(env, line);

    if (STATE_INITIAL.equals(env.state))  {
      parseLineInitial(env, line);
    } else {
      throw new IllegalStateException();
    }
  }

  protected void parseLineInitial(Env env, String line)
  throws DviException, IOException
  {
    env.unrecognized = "";
    int pos = 0;
    while (pos < line.length()) {
      int next = -1;
      if (line.charAt(pos) == '(') {
        int p = -1;
        int p1 = line.indexOf("(", pos+1);
        int p2 = line.indexOf(")", pos+1);
        if (p1 == -1)      p = p2;
        else if (p2 == -1) p = p1;
        else               p = Math.min(p1, p2);
        if (p == -1) p = line.length();

        String filename = line.substring(pos+1, p);

        boolean success = env.handler.isSupportedFileName(env, filename);
//        System.out.println("file: " + filename + ": exists=" + success);
        if (success) {
          env.handler.beginInput(env, filename);
          next = p;
        }
      } else if (line.charAt(pos) == ')') {
        flushUnrecognized(env);
        env.handler.endInput(env);
        next = pos+1;
      } else if (line.charAt(pos) == '[') {
        String a = line.substring(pos+1);
        Matcher mat = Pattern.compile("^(\\d+)").matcher(a);
        if (mat.find()) {
          int count = -1;
          try {
            count = Integer.parseInt(mat.group(1));
          } catch (NumberFormatException e) {
          }
          flushUnrecognized(env);
          env.handler.setCounter(env, count);
          next = pos + 1 + mat.group(1).length();
        }
      } else if (line.charAt(pos) == ']') {
        next = pos+1;
      }

      if (next == -1) {
        env.unrecognized += line.charAt(pos);
        pos++;
      } else {
        pos = next;
      }
    }
    flushUnrecognized(env);
  }

  protected void flushUnrecognized(Env env)
  {
    String str = env.unrecognized.trim();
    env.unrecognized = "";
    if (str != null && str.length() > 0) {
      env.handler.handleUnrecognized(env, str);
    } 
  }

  public static class Env
  {
//    private TeXMessageParser parser;
    private boolean stop;
    private String state;
    private String unrecognized;
    private BufferedReader reader;
    private Handler handler;
    private  String firstLine;
  }

  public static interface Handler
  {
    public boolean isSupportedFileName(Env env, String str);
    public void begin(Env env);
    public void end(Env env);
    public void beginInput(Env env, String filename);
    public void endInput(Env env);
    public void setCounter(Env env, int count);
    public void onEachLine(Env env, String line);
    public void handleUnrecognized(Env env, String line);
  }

  public static abstract class Adaptor
  implements Handler
  {
    public boolean isSupportedFileName(Env env, String str) {
      return false;
    }
    public void begin(Env env) {}
    public void end(Env env) {}
    public void beginInput(Env env, String filename) {}
    public void endInput(Env env) {}
    public void setCounter(Env env, int count) {}
    public void onEachLine(Env env, String line) {}
    public void handleUnrecognized(Env env, String line) {}
  }

  public static class Dumper
  extends Adaptor
  {
    protected final PrintStream out;
    public Dumper(PrintStream out) {
      this.out = out;
    }
    public boolean isSupportedFileName(Env env, String str) {
      return new File(str).exists();
    }
    public void onEachLine(Env env, String line) {
      out.println("############" + line + "########");
    }
    public void beginInput(Env env, String filename) {
      out.println("<input filename=\"" + filename + "\">");
    }
    public void endInput(Env env) {
      out.println("</input>");
    }
    public void handleUnrecognized(Env env, String str) {
      out.println("<unrecognized>" + str + "</unrecognized>");
    }
  }
}
