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

package org.apache.flex.compiler.internal.parsing.as;

import com.google.common.base.Objects;

/**
 * <h3>Background</h3> Imagine an ActionScript source as a character stream
 * (include directives are expanded and merged into the main file), each
 * character's offset number is an "absolute" offset. It differs from the
 * "local" offset of a character in an included file.
 * <p>
 * For example, when "version.as" is included in "Button.as" at offset 10 in
 * "Button.as", the first character in "version.as" has local offset "0", but
 * its "absolute" offset in "Button.as" is "10". Suppose keyword {@code var} is
 * after the {@code include} statement, and its "local" offset is "20", since
 * "version.as" has 100 characters in it, the "absolute" offset of {@code var}
 * becomes "120".
 * <p>
 * <h3>Computing absolute offsets</h3> The {@code OffsetCue} objects are markers
 * on the "absolute" offset axis. It records the adjustment between an "local"
 * offset and its "absolute" offset.
 * <p>
 * The formula is:<br>
 * <code>local = absolute - adjustment</code>
 * <p>
 * The JFlex-generated lexer sets the local offset when it tokenizes a file.
 * Meanwhile, {@code IncludeHandler} records the "adjustment" to compute the
 * "absolute" offset.
 * <p>
 * Besides offset adjustment, an {@code OffsetCue} also records the name of the
 * file the character is from.
 * <p>
 * <h3>Example</h3> Suppose "main.as" includes "A.as"; "A.as" includes "B.as".
 * <p>
 * Imagine the following character stream and the tuples inside parentheses are
 * {@code OffsetCue}.
 * 
 * <pre>
 * --------->        ----------->  ----------->  ------>     ----------->
 * (0:main.as)_______(2:A.as)______(4:B.as)______(6:A.as)____(7:main.as)_______
 * [local]     0   1          0  1         0 1 2 3         2            2  3
 * [absolute]  0   1          2  3         4 5 6 7         8            9  10
 * [file] main.as    in "A"         in "B"        back to "A"   back to "main"
 * </pre>
 * 
 * <ol>
 * <li>{@code 0:main.as} - the root file.</li>
 * <li>{@code 2:A.as} - main.as includes A.as. Local offset "1" in A.as is
 * absolute offset "3" in parsing main.as, because the adjustment is "2";</li>
 * <li>{@code 4:B.as} - A.as includes B.as. Local offset "0" in B.as is absolute
 * offset "4" in parsing main.as, because the adjustment is "4"</li>
 * <li>{@code 6:A.as} - return to A.as from B.as. The adjustments change because
 * characters in B.as advance absolute offsets.</li>
 * <li>{@code 7:main.as} - return to main.as from A.as.</li>
 * </ol>
 * An {@code OffsetCue} applies to all the characters/tokens after itself
 * (inclusive) until the next {@code OffsetCue} (exclusive).
 * <p>
 * {@code OffsetCue} is an immutable class. It is created in
 * {@link IncludeHandler}.
 */
public class OffsetCue
{
    protected OffsetCue(String filename, int absolute, int adjustment)
    {
        assert filename != null : "filename can't be null";
        assert absolute >= 0 : "absolute offset can't be negative";
        assert adjustment >= 0 : "adjustment can't be negative";

        this.filename = filename;
        this.absolute = absolute;
        this.adjustment = adjustment;
        this.local = absolute - adjustment;
    }

    /**
     * File that contains the characters after this cue point until the next cue
     * point.
     */
    public final String filename;

    /**
     * Adjustment between absolute offset and local offset.<br>
     * <code>local = absolute - adjustment</code>
     */
    public final int adjustment;

    /**
     * Absolute offset.
     */
    public final int absolute;

    /**
     * Local offset.
     */
    public final int local;

    @Override
    public String toString()
    {
        return Objects
                .toStringHelper(this)
                .add("absolute", absolute)
                .add("adjustment", adjustment)
                .add("local", local)
                .add("filename", filename)
                .toString();
    }
}
