/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* $Id$ */

package org.apache.fop.complexscripts.fonts;

import java.io.File;
import java.nio.IntBuffer;
import java.util.Collections;

import org.junit.Test;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;

import org.apache.fop.apps.io.InternalResourceResolver;
import org.apache.fop.apps.io.ResourceResolverFactory;
import org.apache.fop.complexscripts.fonts.GlyphTable.LookupTable;
import org.apache.fop.complexscripts.fonts.ttx.TTXFile;
import org.apache.fop.complexscripts.util.GlyphContextTester;
import org.apache.fop.complexscripts.util.GlyphSequence;
import org.apache.fop.complexscripts.util.ScriptContextTester;
import org.apache.fop.fonts.MultiByteFont;
import org.apache.fop.fonts.truetype.OFFontLoader;

public class GSUBTestCase implements ScriptContextTester, GlyphContextTester {

    private static String ttxFilesRoot = "test/resources/complexscripts";

    private static String[][] ttxFonts = {
        { "f0", "arab/ttx/arab-001.ttx" },              // simplified arabic
        { "f1", "arab/ttx/arab-002.ttx" },              // traditional arabic
        { "f2", "arab/ttx/arab-003.ttx" },              // lateef
        { "f3", "arab/ttx/arab-004.ttx" },              // scheherazade
    };

    @Test
    public void testGSUBSingle() throws Exception {
        performSubstitutions(GSUBData.ltSingle);
    }

    @Test
    public void testGSUBMultiple() throws Exception {
        performSubstitutions(GSUBData.ltMultiple);
    }

    @Test
    public void testGSUBAlternate() throws Exception {
        performSubstitutions(GSUBData.ltAlternate);
    }

    @Test
    public void testGSUBLigature() throws Exception {
        performSubstitutions(GSUBData.ltLigature);
    }

    @Test
    public void testGSUBContextual() throws Exception {
        performSubstitutions(GSUBData.ltContextual);
    }

    @Test
    public void testGSUBChainedContextual() throws Exception {
        performSubstitutions(GSUBData.ltChainedContextual);
    }

    /**
     * Perform substitutions on all test data in test specification TS.
     * @param ts test specification
     */
    private void performSubstitutions(Object[][] ts) {
        assert ts.length > 0;
        Object[] tp = ts[0];
        for (int i = 1; i < ts.length; i++) {
            performSubstitutions(tp, ts[i]);
        }
    }

    /**
     * Perform substitutions on all test data TD using test parameters TP.
     * @param tp test parameters
     * @param td test data
     */
    private void performSubstitutions(Object[] tp, Object[] td) {
        assert tp.length > 0;
        if (td.length > 5) {
            String fid = (String) td[0];
            String lid = (String) td[1];
            String script = (String) td[2];
            String language = (String) td[3];
            String feature = (String) td[4];
            TTXFile tf = findTTX(fid);
            assertTrue(tf != null);
            GlyphSubstitutionTable gsub = tf.getGSUB();
            assertTrue(gsub != null);
            GlyphSubstitutionSubtable[] sta = findGSUBSubtables(gsub, script, language, feature, lid);
            assertTrue(sta != null);
            assertTrue(sta.length > 0);
            ScriptContextTester sct = findScriptContextTester(script, language, feature);
            String[][][] tia = (String[][][]) td[5];            // test instance array
            for (String[][] ti : tia) {                       // test instance
                if (ti != null) {
                    if (ti.length > 1) {                      // must have at least input and output glyph id arrays
                        String[] igia = ti[0];                  // input glyph id array
                        String[] ogia = ti[1];                  // output glyph id array
                        GlyphSequence igs = tf.getGlyphSequence(igia);
                        GlyphSequence ogs = tf.getGlyphSequence(ogia);
                        GlyphSequence tgs =
                                GlyphSubstitutionSubtable.substitute(igs, script, language, feature, sta, sct);
                        assertSameGlyphs(ogs, tgs);
                    }
                }
            }
        }
    }

    private String findTTXPath(String fid) {
        for (String[] fs : ttxFonts) {
            if ((fs != null) && (fs.length > 1)) {
                if (fs[0].equals(fid)) {
                    return ttxFilesRoot + File.separator + fs[1];
                }
            }
        }
        return null;
    }

    private TTXFile findTTX(String fid) {
        String pn = findTTXPath(fid);
        assertTrue(pn != null);
        try {
            TTXFile tf = TTXFile.getFromCache(pn);
            return tf;
        } catch (Exception e) {
            fail(e.getMessage());
            return null;
        }
    }

    private GlyphSubstitutionSubtable[] findGSUBSubtables(
            GlyphSubstitutionTable gsub, String script, String language, String feature, String lid) {
        LookupTable lt = gsub.getLookupTable(lid);
        if (lt != null) {
            return (GlyphSubstitutionSubtable[]) lt.getSubtables();
        } else {
            return null;
        }
    }

    private ScriptContextTester findScriptContextTester(String script, String language, String feature) {
        return this;
    }

    public GlyphContextTester getTester(String feature) {
        return this;
    }

    public boolean test(String script, String language, String feature, GlyphSequence gs, int index, int flags) {
        return true;
    }

    private void assertSameGlyphs(GlyphSequence gs1, GlyphSequence gs2) {
        assertNotNull(gs1);
        assertNotNull(gs2);
        IntBuffer gb1 = gs1.getGlyphs();
        IntBuffer gb2 = gs2.getGlyphs();
        assertEquals("unequal glyph count", gb1.limit(), gb2.limit());
        for (int i = 0; i < gb1.limit(); i++) {
            int g1 = gb1.get(i);
            int g2 = gb2.get(i);
            assertEquals("unequal glyph code", g1, g2);
        }
    }

    @Test
    public void testCreateClassTable() {
        GlyphCoverageTable coverageTable = GlyphCoverageTable.createCoverageTable(null);
        GlyphClassTable classTable = GlyphClassTable.createClassTable(Collections.singletonList(coverageTable));
        assertNotNull(classTable);
    }

    @Test
    public void testSubRuleSets() throws Exception {
        File fontfile = new File("test/resources/fonts/ttf/DejaVuLGCSerif.ttf");
        InternalResourceResolver resourceResolver =
                ResourceResolverFactory.createDefaultInternalResourceResolver(new File(".").toURI());
        OFFontLoader fontLoader = new OFFontLoader(fontfile.toURI(), resourceResolver);
        MultiByteFont font = (MultiByteFont) fontLoader.getFont();
        String in = "\u006A\u0301";
        String out = "\u0237\u0301";
        String sub = font.performSubstitution(in, "latn", "dflt", null, false).toString();
        assertEquals(sub, out);
    }
}
