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

import java.io.File;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import dvi.DviFontSpec;
import dvi.DviRegister;
import dvi.api.DviContextSupport;
import dvi.cmd.DviBop;
import dvi.render.DefaultDevicePainter;

public class PostScriptSpecialParser
extends DefaultDevicePainter
{
  private static final Logger LOGGER = Logger.getLogger(PostScriptSpecialParser.class.getName());
  
  private static final Pattern patPS
    = Pattern.compile(
      "\\s*ps:\\s*(.*)",
      Pattern.CASE_INSENSITIVE
    );
  private static final Pattern patHeader
    = Pattern.compile(
      "\\s*header\\s*=\\s*(.*)",
      Pattern.CASE_INSENSITIVE
    );
  private static final Pattern patPSFile
    = Pattern.compile(
      "\\s*PSFile\\s*=\\s*([^\\s]*)\\s+(.*)\\s*",
      Pattern.CASE_INSENSITIVE
    );
  
  public PostScriptSpecialParser(DviContextSupport dcs)
  {
    super(dcs);
  }
  
  private File workDir;
  public void setWorkDirectory(File dir)
  {
    this.workDir = dir;
  }
  public File getWorkDirectory()
  {
    return workDir;
  }

  private final EmbeddedPostScript eps = new EmbeddedPostScript();

  public EmbeddedPostScript getEmbeddedPostScript()
  {
    return eps;
  }

  private int pageNum = 0;

  public void beginPage(DviBop bop)
  {
    eps.beginPage(pageNum);
  }

  public void endPage()
  {
    eps.endPage();
    pageNum++;
  }

  public void beginFont(DviFontSpec fs)
  {
  }

  public void endFont()
  {
  }

  ////////////

  public void drawChar(int code)
  {
    // ignored.
  }

  public void drawRule(int w, int h)
  {
    // ignored.
  }
  
  protected String toAbsoluteFilename(String filename)
  {
    File file = new File(workDir, filename);
    LOGGER.finer("Resolved filename=" + filename + " to " + file.getAbsolutePath());
    return file.getAbsolutePath();
  }

  public void drawSpecial(byte [] _xxx)
  {
    String xxx = new String(_xxx);
    Matcher mat;

    LOGGER.finer("handling special: " + xxx);
    if (xxx.startsWith("\"")) {
      DviRegister reg = getGeometerContext().getRegister();
      eps.add(
        new EmbeddedPostScript.QuoteSpecial(
          reg.getH(), reg.getV(),
          getDviUnit(),
          xxx.substring(1).trim()
        )
      );
    } else if (xxx.startsWith("!")) {
      eps.add(
        new EmbeddedPostScript.BangSpecial(
          xxx.substring(1).trim()
        )
      );
    } else if ((mat = patPS.matcher(xxx)).matches()) {
      DviRegister reg = getGeometerContext().getRegister();
      eps.add(
        new EmbeddedPostScript.PSSpecial(
          reg.getH(), reg.getV(),
          getDviUnit(),
          mat.group(1)
        )
      );
    } else if ((mat = patHeader.matcher(xxx)).matches()) {
      LOGGER.finer("PSHeader special found: " + xxx);
      eps.add(
        new EmbeddedPostScript.HeaderSpecial(
          trimQuotes(mat.group(1))
        )
      );
    } else if ((mat = patPSFile.matcher(xxx)).matches()) {
      LOGGER.finer("page=" + pageNum + " PSFile special found: " + xxx);
      String fileName = toAbsoluteFilename(trimQuotes(mat.group(1)));
      String options = mat.group(2);
      int llx = 0;
      int lly = 0;
      int urx = 0;
      int ury = 0;
      int rwi = 0;
      int rhi = 0;
      int angle = 0;
      String [] os = Pattern.compile("\\s+").split(options);
      for (int i=0; i<os.length; i++) {
        Matcher mat2 = Pattern.compile("([a-z]+)=(-?[.0-9]+)").matcher(os[i]);
        if (mat2.matches()) {
          try {
            String var = mat2.group(1);
            int val = Integer.parseInt(mat2.group(2));
            if ("llx".equals(var)) llx = val;
            if ("lly".equals(var)) lly = val;
            if ("urx".equals(var)) urx = val;
            if ("ury".equals(var)) ury = val;
            if ("rwi".equals(var)) rwi = val;
            if ("rhi".equals(var)) rhi = val;
            if ("angle".equals(var)) angle = val;
          } catch (NumberFormatException ex) {
            // ignored.
          }
        }
      }
      DviRegister reg = getGeometerContext().getRegister();
      eps.add(
        new EmbeddedPostScript.PSFileSpecial(
          reg.getH(), reg.getV(),
          getDviUnit(),
          fileName,
          llx, lly, urx, ury, rwi, rhi, angle
        )
      );
    }
    /* Support other types of specials:
    } else if (xxx.equals("bk")) {
    } else if (xxx.equals("fp")) {
    } else if (xxx.startsWith("pn ")) {
    } else if (xxx.startsWith("ar ")) {
    } else if (xxx.startsWith("pa ")) {
    } else if (xxx.startsWith("dt ")) {

    } else if (xxx.startsWith("em:")) {
    } else if (xxx.startsWith("HP:")) {
    */

  }

  private static String trimQuotes(String str) {
    if (str == null) return null;
    while (str.startsWith("\"")) {
      str = str.substring(1);
    }
    while (str.endsWith("\"")) {
      str = str.substring(0, str.length()-1);
    }
    return str;
  }
}
