// $Id: Version.java,v 1.8 2004/02/27 16:30:00 m31 Exp $
//
// This file is part of the program suite "Principia Mathematica II"
// which is a working prototype for the main project:
// "Hilbert II" - http://www.qedeq.org
//
// Copyright 2001-2004  Michael Meyling <mime@qedeq.org>.
//
// "Principia Mathematica II" is free software; you can redistribute
// it and/or modify it under the terms of the GNU General Public
// License as published by the Free Software Foundation; either
// version 2 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.

package com.meyling.principia.module;


import com.meyling.principia.argument.AbstractArgumentList;
import com.meyling.principia.argument.Argument;
import com.meyling.principia.argument.ArgumentException;
import com.meyling.principia.argument.Text;
import com.meyling.principia.io.IoConstants;


/**
 * A version description.
 *
 * @version $Revision: 1.8 $
 * @author  Michael Meyling
 */
public class Version extends AbstractArgumentList
        implements Argument, Textable {


    /**
     * all version numbers
     */
    private final int[] numbers;


    /**
     * Constructs a version description.
     *
     * @param   arguments     {@link Text} to be a version description
     * @throws  ArgumentException    if there is not exactly one
     *          argument or the first argument is not an instance
     *          of {@link Text} or it isn't build of digits and
     *          periods in the following form: n.nn.nn(.nn)*
     */
    public Version(final Argument[] arguments)
            throws ArgumentException {
        super(arguments);

        if (arguments.length != 1) {
            throw new ArgumentException(
                ModuleConstants.CODE_CONSTRUCTOR_WRONG_ARGUMENT_NUMBER,
                ModuleCreator.getName(this.getClass())
                + ModuleConstants.OPERATOR_WITH_ONE_ARGUMENT);
        }
        if (!(arguments[0] instanceof Text)) {
            throw new ArgumentException(
                ModuleConstants.CODE_CONSTRUCTOR_WRONG_ARGUMENT_TYPE,
                ModuleCreator.getName(this.getClass())
                + ModuleConstants.FIRST_ARGUMENT_TYPE
                + ModuleConstants.QUOTED_STRING,
                arguments[0]);
        }
        final String version = ((Text) arguments[0]).getText();
        if (version.length() == 0) {
            throw new ArgumentException(
                ModuleConstants.CODE_CONSTRUCTOR_WRONG_ARGUMENT_TYPE,
                ModuleCreator.getName(this.getClass())
                + ModuleConstants.NO_EMPTY_STRING,
                arguments[0]);
        }
        if (version.length() < 7
                || !Character.isDigit(version.charAt(0))
                || version.charAt(1) != '.'
                || !Character.isDigit(version.charAt(2))
                || !Character.isDigit(version.charAt(3))
                || version.charAt(4) != '.'
                || !Character.isDigit(version.charAt(5))
                || !Character.isDigit(version.charAt(6))) {
            throw new ArgumentException(
                ModuleConstants.CODE_CONSTRUCTOR_WRONG_ARGUMENT_TYPE,
                ModuleCreator.getName(this.getClass())
                + ModuleConstants.MUST_BE_DIGIT_PERIOD_STRING,
                arguments[0]);
        }
        int periods = 2;
        for (int i = 7; i < version.length(); i += 3) {
            if (i < version.length() + 3
                || version.charAt(i) != '.'
                || !Character.isDigit(version.charAt(i + 1))
                || !Character.isDigit(version.charAt(i + 2))) {
                throw new ArgumentException(
                    ModuleConstants.CODE_CONSTRUCTOR_WRONG_ARGUMENT_TYPE,
                    ModuleCreator.getName(this.getClass())
                    + ModuleConstants.MUST_BE_DIGIT_PERIOD_STRING,
                    arguments[0]);
            }
            periods++;
        }
        this.numbers = new int[periods + 1];
        int m = 0;
        int k = 0;
        try {
            int i;
            for (i = 0; i < version.length(); i++) {
                if (version.charAt(i) == '.') {
                    this.numbers[k++]
                        = Integer.parseInt(version.substring(m, i));
                    m = i + 1;
                }
            }
            this.numbers[k]
                = Integer.parseInt(version.substring(m));
        } catch (NumberFormatException e) {
            throw new ArgumentException(
                ModuleConstants.CODE_CONSTRUCTOR_WRONG_ARGUMENT_TYPE,
                ModuleCreator.getName(this.getClass())
                + " " + IoConstants.INTEGER_TOO_BIG,
                arguments[0]);
        }

    }


    /**
     * Constructs a version description.
     *
     * @param   version     version
     * @throws  ArgumentException   if version it isn't build of digits and
     *          periods in the following form: n.nn.nn(.nn)*
     */
    public Version(final String version)
            throws ArgumentException {
        this(new Argument[] { new Text(version) });
    }


    /**
     * Check if this version is greater than another one
     *
     * @param   v   version to compare with
     * @return  is this version greater than <code>v</code>?
     */
    public final boolean greaterThan(final Version v) {
        int max = this.numbers.length;
        if (v.numbers.length > max) {
            max = v.numbers.length;
        }
        for (int i = 0; i < max; i++) {
            if (this.getSubVersion(i) != v.getSubVersion(i)) {
                return this.getSubVersion(i) > v.getSubVersion(i);
            }
        }
        return false;   // equal
    }


    /**
     * Check if this version is equal to another one.
     * This method would return true for the versions
     * &quot;1.001.00&quot; and &quot;1.1&quot;.
     *
     * @param   v   version to compare with
     * @return  is this version the same as <code>v</code>?
     */
    public final boolean equalWith(final Version v) {
        int max = this.numbers.length;
        if (v.numbers.length > max) {
            max = v.numbers.length;
        }
        for (int i = 0; i < max; i++) {
            if (this.getSubVersion(i) != v.getSubVersion(i)) {
                return false;
            }
        }
        return true;   // equal
    }


    /**
     * Get the <code>i-th</code> sub version.
     *
     * @param   i   get this sub version
     * @return  value of <code>i-th</code> sub version
     */
    private final int getSubVersion(final int i) {
        if (i < numbers.length) {
            return numbers[i];
        } else {
            return 0;
        }
    }


    /** @inherited */
    public final String getText() {
        return ((Text) getArgument(0)).getText();
    }


    /** @inherited */
    public final Argument create(final Argument[] arguments)
            throws ArgumentException {
        return new Version(arguments);
    }


    /** @inherited */
    public final String toString() {
        return "Version " + getArgument(0).toString();
    }


}
