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

import org.apache.fop.complexscripts.fonts.GlyphDefinitionTable;
import org.apache.fop.complexscripts.util.CharAssociation;
import org.apache.fop.complexscripts.util.GlyphSequence;
import org.apache.fop.complexscripts.util.ScriptContextTester;

// CSOFF: LineLengthCheck

/**
 * <p>Default script processor, which enables default glyph composition/decomposition, common ligatures, localized forms
 * and kerning.</p>
 *
 * <p>This work was originally authored by Glenn Adams (gadams@apache.org).</p>
 */
public class DefaultScriptProcessor extends ScriptProcessor {

    /** features to use for substitutions */
    private static final String[] GSUB_FEATURES =
    {
        "ccmp",                                                 // glyph composition/decomposition
        "liga",                                                 // common ligatures
        "locl"                                                  // localized forms
    };

    /** features to use for positioning */
    private static final String[] GPOS_FEATURES =
    {
        "kern",                                                 // kerning
        "mark",                                                 // mark to base or ligature positioning
        "mkmk"                                                  // mark to mark positioning
    };

    DefaultScriptProcessor(String script) {
        super(script);
    }

    @Override
    /** {@inheritDoc} */
    public String[] getSubstitutionFeatures() {
        return GSUB_FEATURES;
    }

    @Override
    /** {@inheritDoc} */
    public ScriptContextTester getSubstitutionContextTester() {
        return null;
    }

    @Override
    /** {@inheritDoc} */
    public String[] getPositioningFeatures() {
        return GPOS_FEATURES;
    }

    @Override
    /** {@inheritDoc} */
    public ScriptContextTester getPositioningContextTester() {
        return null;
    }

    @Override
    /** {@inheritDoc} */
    public GlyphSequence reorderCombiningMarks(GlyphDefinitionTable gdef, GlyphSequence gs, int[] unscaledWidths, int[][] gpa, String script, String language) {
        int   ng  = gs.getGlyphCount();
        int[] ga  = gs.getGlyphArray(false);
        int   nm  = 0;
        // count combining marks
        for (int i = 0; i < ng; i++) {
            int gid = ga [ i ];
            int gw = unscaledWidths [ i ];
            if (isReorderedMark(gdef, ga, unscaledWidths, i)) {
                nm++;
            }
        }
        // only reorder if there is at least one mark and at least one non-mark glyph
        if ((nm > 0) && ((ng - nm) > 0)) {
            CharAssociation[] aa = gs.getAssociations(0, -1);
            int[] nga = new int [ ng ];
            int[][] npa = (gpa != null) ? new int [ ng ][] : null;
            CharAssociation[] naa = new CharAssociation [ ng ];
            int k = 0;
            CharAssociation ba = null;
            int bg = -1;
            int[] bpa = null;
            for (int i = 0; i < ng; i++) {
                int gid = ga [ i ];
                int[] pa = (gpa != null) ? gpa [ i ] : null;
                CharAssociation ca = aa [ i ];
                if (isReorderedMark(gdef, ga, unscaledWidths, i)) {
                    nga [ k ] = gid;
                    naa [ k ] = ca;
                    if (npa != null) {
                        npa [ k ] = pa;
                    }
                    k++;
                } else {
                    if (bg != -1) {
                        nga [ k ] = bg;
                        naa [ k ] = ba;
                        if (npa != null) {
                            npa [ k ] = bpa;
                        }
                        k++;
                        bg = -1;
                        ba = null;
                        bpa = null;
                    }
                    if (bg == -1) {
                        bg = gid;
                        ba = ca;
                        bpa = pa;
                    }
                }
            }
            if (bg != -1) {
                nga [ k ] = bg;
                naa [ k ] = ba;
                if (npa != null) {
                    npa [ k ] = bpa;
                }
                k++;
            }
            assert k == ng;
            if (npa != null) {
                System.arraycopy(npa, 0, gpa, 0, ng);
            }
            return new GlyphSequence(gs, null, nga, null, null, naa, null);
        } else {
            return gs;
        }
    }

    protected boolean isReorderedMark(GlyphDefinitionTable gdef, int[] glyphs, int[] unscaledWidths, int index) {
        return gdef.isGlyphClass(glyphs[index], GlyphDefinitionTable.GLYPH_CLASS_MARK) && (unscaledWidths[index] != 0);
    }

}
