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

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.nio.IntBuffer;
import java.util.List;

import org.junit.Test;

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

import org.apache.fop.complexscripts.fonts.GlyphPositioningTable;
import org.apache.fop.complexscripts.fonts.GlyphSubstitutionTable;
import org.apache.fop.complexscripts.fonts.ttx.TTXFile;
import org.apache.fop.complexscripts.util.GlyphSequence;

// CSOFF: LineLength

/**
 * Tests for functionality related to the arabic script.
 */
public class ArabicWordFormsTestCase implements ArabicWordFormsConstants {

    @Test
    public void testArabicWordForms() {
        for (String sfn : SRC_FILES) {
            try {
                processWordForms(new File(DAT_FILES_DIR));
            } catch (Exception e) {
                fail(e.getMessage());
            }
        }
    }

    private void processWordForms(File dfd) {
        String[] files = listWordFormFiles(dfd);
        for (String fn : files) {
            File dff = new File(dfd, fn);
            processWordForms(dff.getAbsolutePath());
        }
    }

    private String[] listWordFormFiles(File dfd) {
        return dfd.list(new FilenameFilter() {
                public boolean accept(File f, String name) {
                    return hasPrefixFrom(name, SRC_FILES) && hasExtension(name, WF_FILE_DAT_EXT);
                }
                private boolean hasPrefixFrom(String name, String[] prefixes) {
                    for (String p : prefixes) {
                        if (name.startsWith(p)) {
                            return true;
                        }
                    }
                    return false;
                }
                private boolean hasExtension(String name, String extension) {
                    return name.endsWith("." + extension);
                }
            });
    }

    private void processWordForms(String dpn) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(dpn);
            if (fis != null) {
                ObjectInputStream ois = new ObjectInputStream(fis);
                List<Object[]> data = (List<Object[]>) ois.readObject();
                if (data != null) {
                    processWordForms(data);
                }
                ois.close();
            }
        } catch (FileNotFoundException e) {
            throw new RuntimeException(e.getMessage(), e);
        } catch (IOException e) {
            throw new RuntimeException(e.getMessage(), e);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        } finally {
            if (fis != null) {
                try { fis.close(); } catch (Exception e) { /* NOP */ }
            }
        }
    }

    private void processWordForms(List<Object[]> data) {
        assert data != null;
        assert data.size() > 0;
        String script = null;
        String language = null;
        String tfn = null;
        TTXFile tf = null;
        GlyphSubstitutionTable gsub = null;
        GlyphPositioningTable gpos = null;
        int[] widths = null;
        for (Object[] d : data) {
            if (script == null) {
                assert d.length >= 4;
                script = (String) d[0];
                language = (String) d[1];
                tfn = (String) d[3];
                tf = TTXFile.getFromCache(TTX_FONTS_DIR + File.separator + tfn);
                assertTrue(tf != null);
                gsub = tf.getGSUB();
                assertTrue(gsub != null);
                gpos = tf.getGPOS();
                assertTrue(gpos != null);
                widths = tf.getWidths();
                assertTrue(widths != null);
            } else {
                assert tf != null;
                assert gsub != null;
                assert gpos != null;
                assert tfn != null;
                assert d.length >= 4;
                String wf = (String) d[0];
                int[] iga = (int[]) d[1];
                int[] oga = (int[]) d[2];
                int[][] paa = (int[][]) d[3];
                GlyphSequence tigs = tf.mapCharsToGlyphs(wf);
                assertSameGlyphs(iga, getGlyphs(tigs), "input glyphs", wf, tfn);
                GlyphSequence togs = gsub.substitute(tigs, script, language);
                assertSameGlyphs(oga, getGlyphs(togs), "output glyphs", wf, tfn);
                int[][] tpaa = new int [ togs.getGlyphCount() ] [ 4 ];
                if (gpos.position(togs, script, language, 1000, widths, tpaa)) {
                    assertSameAdjustments(paa, tpaa, wf, tfn);
                } else if (paa != null) {
                    assertEquals("unequal adjustment count, word form(" + wf + "), font (" + tfn + ")", paa.length, 0);
                }
            }
        }
    }

    private void assertSameGlyphs(int[] expected, int[] actual, String label, String wf, String tfn) {
        assertEquals(label + ": unequal glyph count, word form(" + wf + "), font (" + tfn + ")", expected.length, actual.length);
        for (int i = 0, n = expected.length; i < n; i++) {
            int e = expected[i];
            int a = actual[i];
            assertEquals(label + ": unequal glyphs[" + i + "], word form(" + wf + "), font (" + tfn + ")", e, a);
        }
    }

    private void assertSameAdjustments(int[][] expected, int[][] actual, String wf, String tfn) {
        assertEquals("unequal adjustment count, word form(" + wf + "), font (" + tfn + ")", expected.length, actual.length);
        for (int i = 0, n = expected.length; i < n; i++) {
            int[] ea = expected[i];
            int[] aa = actual[i];
            assertEquals("bad adjustments length, word form(" + wf + "), font (" + tfn + ")", ea.length, aa.length);
            for (int k = 0; k < 4; k++) {
                int e = ea[k];
                int a = aa[k];
                assertEquals("unequal adjustment[" + i + "][" + k + "], word form(" + wf + "), font (" + tfn + ")", e, a);
            }
        }
    }

    private static int[] getGlyphs(GlyphSequence gs) {
        IntBuffer gb = gs.getGlyphs();
        int[] ga = new int [ gb.limit() ];
        gb.rewind();
        gb.get(ga);
        return ga;
    }

}
