/*
 * 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.FilenameFilter;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.io.ObjectInputStream;
import java.nio.IntBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;

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;

import org.junit.BeforeClass;
import org.junit.Test;

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

/**
 * Tests for functionality related to the arabic script.
 */
public class ArabicTestCase implements ArabicTestConstants {

    @Test
    public void testArabicWordForms() {
        for ( String sfn : srcFiles ) {
            try {
                processWordForms ( new File ( datFilesDir ) );
            } 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, srcFiles ) && 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 ) {}
            }
        }
    }

    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 ( ttxFontsDir + 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;
    }

}
