
package uk.co.wingpath.registration;

import java.math.BigInteger;
import java.util.Random;
import uk.co.wingpath.io.SerialConnection;
import uk.co.wingpath.util.Installer;

class Details
{
    public Key key;         // registration key - decoded.

    public String user;     // user name - should be consistent with key
    public String company;  // company name - should be consistent with key
    public String newUser;  // user name - entered by user, but we don't
                            // yet have corresponding key
    public String newCompany; // company name - entered by user, but we
                            // don't yet have corresponding key
    public int product;     // product code
    public int majorVersion; // product major version
    public int minorVersion; // product minor version
    public int modTime;     // number stored in low-order part of
                            // registration file modification time
    public int random;      // random number - stored in "funny" file
    public int timeLast;    // time that details were last checked (seconds)
    public int timeLeft;    // time left for evaluation (seconds)
    int modTime1;
    int modTime2;
    public int status;      // summary of state of registration
    public boolean writeRequired;

    public static final int NEW = 0;
    public static final int CORRUPT = 1;
    public static final int EVAL = 2;
    public static final int EXPIRED = 3;
    public static final int FULL = 4;
    public static final int DEREGISTERED = 5;
    public static final int EXPIREDF = 6;

    int javaVersion;
    int os;
    int bits;
    int vendor;
    int hasSerial;

    // Construct initial registration details object.
    // Fill in Java version and OS details, so they can be passed
    // via product ID back to web site.
    // modTime is effectively a small random number, to be used as
    // low-order part of registration file modification time, and also
    // passed in product ID to web-site. It gets incorporated in
    // registration key so that we check whether registration file
    // has been copied.
    // 'random' a small random number, stored in the funny file, and also
    // passed in product ID to web-site where it gets incorporated in
    // registration key.
    // The user & host names and the registration key string will be filled
    // in later, either by being read from the registration file or by
    // being entered by the user.

    public Details (int product, int majorVersion)
    {
        assert product >= 1 && product < Crypt.MAX_PRODUCT : product;
        assert majorVersion >= 1 && majorVersion < Crypt.MAX_MAJOR_VERSION :
            majorVersion;
        user = "";
        company = "";
        newUser = "";
        newCompany = "";
        key = null;
        status = NEW;
        writeRequired = false;
        timeLast = 0;
        timeLeft = 0;
        this.product = product;
        this.majorVersion = majorVersion;
        minorVersion = 0;

        modTime = (int) ((System.currentTimeMillis () / 1000)
            % Crypt.MAX_MODTIME);
        modTime1 = modTime;
        modTime2 = modTime;

        Random r = new Random ();
        random = r.nextInt (Crypt.MAX_RANDOM);

        javaVersion = Installer.getJavaVersion ();
        javaVersion %= Crypt.MAX_JAVAVERSION;

        String osName = System.getProperty ("os.name").toLowerCase ();
        if (osName.contains ("linux"))
            os = 1;
        else if (osName.contains ("solaris"))
            os = 3;
        else if (osName.contains ("sunos"))
            os = 4;
        else if (osName.contains ("mac"))
            os = 5;
        else if (osName.contains ("aix"))
            os = 6;
        else if (osName.contains ("freebsd"))
            os = 7;
        else if (osName.contains ("windows 2000"))
            os = 8;
        else if (osName.contains ("windows 95"))
            os = 9;
        else if (osName.contains ("windows 98"))
            os = 10;
        else if (osName.contains ("windows nt"))
            os = 11;
        else if (osName.contains ("windows xp"))
            os = 12;
        else if (osName.contains ("windows 8"))
            os = 13;
        else if (osName.contains ("windows vista"))
            os = 14;
        else if (osName.contains ("windows me"))
            os = 15;
        else if (osName.contains ("windows 2003"))
            os = 16;
        else if (osName.contains ("windows server 2008"))
            os = 17;
        else if (osName.contains ("windows 7"))
            os = 18;
        else if (osName.contains ("windows 10"))
            os = 19;
        else if (osName.contains ("windows"))
            os = 2;
        else
            os = 0;

        String dataModel = System.getProperty ("sun.arch.data.model");
        if (dataModel == null)
            dataModel = System.getProperty ("com.ibm.vm.bitmode");
        if (dataModel == null)
            bits = 0;
        else if (dataModel.equals ("32"))
            bits = 1;
        else if (dataModel.equals ("64"))
            bits = 2;
        else
            bits = 0;

        hasSerial = SerialConnection.isAvailable () ? 2 : 1;

        String vendorName = System.getProperty ("java.vendor");
        if (vendorName.contains ("Sun"))
            vendor = 1;
        else if (vendorName.contains ("IBM"))
            vendor = 2;
        else if (vendorName.contains ("Apple"))
            vendor = 3;
        else if (vendorName.contains ("Oracle"))
            vendor = 4;
        else if (vendorName.contains ("Free Software Foundation"))
            vendor = 5;
        else
            vendor = 0;
    }

    // Construct a product ID from the new details entered by the user

    String getProductId (Details previousDetails)
    {
        assert product >= 1 && product < Crypt.MAX_PRODUCT : product;
        assert majorVersion >= 1 && majorVersion < Crypt.MAX_MAJOR_VERSION :
            majorVersion;
        assert minorVersion >= 0 && minorVersion < Crypt.MAX_MINOR_VERSION :
            minorVersion;
        int previousLogId = 0;
        int previousVersion = 0;
        int previousLevel = 0;
        if (previousDetails != null)
        {
            previousLogId = previousDetails.key.logId;
            previousVersion = previousDetails.key.majorVersion;
            previousLevel = previousDetails.key.licenceLevel;
        }
        int hash = Crypt.hash (newUser + newCompany);
        int logId = key == null ? 0 : key.logId;
/*
System.out.println ("getProductId: " +
" product " + product +
" majorVersion " + majorVersion +
" minorVersion " + minorVersion +
" modTime " + modTime +
" hash " + hash +
" javaVersion " + javaVersion +
" os " + os +
" bits " + bits +
" hasSerial " + hasSerial +
" vendor " + vendor +
" logId " + logId +
" status " + status +
" previousVersion " + previousVersion +
" previousLogId " + previousLogId);
*/

        BigInteger n = BigInteger.ZERO;
        int date = (int) (System.currentTimeMillis () / (1000 * 60 * 60 * 24));
        n = n.multiply (Crypt.maxLicenceLevel);
        n = n.add (BigInteger.valueOf (previousLevel));
        n = n.multiply (Crypt.maxDate);
        n = n.add (BigInteger.valueOf (date));
        n = n.multiply (Crypt.maxHasSerial);
        n = n.add (BigInteger.valueOf (hasSerial));
        n = n.multiply (Crypt.maxRandom);
        n = n.add (BigInteger.valueOf (random));
        n = n.multiply (Crypt.maxLogId);
        n = n.add (BigInteger.valueOf (previousLogId % Crypt.MAX_LOGID));
        n = n.multiply (Crypt.maxMajorVersion);
        n = n.add (BigInteger.valueOf (previousVersion));
        n = n.multiply (BigInteger.valueOf (Crypt.MAX_MAGIC));
        n = n.add (BigInteger.valueOf (Crypt.MAGIC_PRODUCTID));
        n = n.multiply (Crypt.maxStatus);
        n = n.add (BigInteger.valueOf (status));
        n = n.multiply (Crypt.maxLogId);
        n = n.add (BigInteger.valueOf (logId % Crypt.MAX_LOGID));
        n = n.multiply (Crypt.maxVendor);
        n = n.add (BigInteger.valueOf (vendor));
        n = n.multiply (Crypt.maxOS);
        n = n.add (BigInteger.valueOf (os));
        n = n.multiply (Crypt.max64Bit);
        n = n.add (BigInteger.valueOf (bits));
        n = n.multiply (Crypt.maxJavaVersion);
        n = n.add (BigInteger.valueOf (javaVersion));
        n = n.multiply (Crypt.maxHash);
        n = n.add (BigInteger.valueOf (hash));
        n = n.multiply (Crypt.maxModTime);
        n = n.add (BigInteger.valueOf (modTime));
        n = n.multiply (Crypt.maxMajorVersion);
        n = n.add (BigInteger.valueOf (majorVersion));
        n = n.multiply (Crypt.maxMinorVersion);
        n = n.add (BigInteger.valueOf (minorVersion));
        n = n.multiply (Crypt.maxProduct);
        n = n.add (BigInteger.valueOf (product));
// System.out.println ("  " + n.toString (16));
        String str = "" + Crypt.CHAR_VERSION_CURRENT + Crypt.CHAR_PRODUCTID +
            Crypt.encrypt (n, Crypt.CHAR_VERSION_E);
        return str;
    }

    void setStatus ()
    {
        if (key == null)
        {
// System.out.println ("setStatus: NEW");
            status = NEW;
        }
        else
        {
            switch (key.expiry)
            {
            case Crypt.FULL_LICENCE:
// System.out.println ("setStatus: FULL");
                status = FULL;
                break;
            case Crypt.NEW_LICENCE:
// System.out.println ("setStatus: NEW");
                status = NEW;
                break;
            case Crypt.EXPIRED_LICENCE:
// System.out.println ("setStatus: EXPIRED");
                status = EXPIRED;
                break;
            case Crypt.EXPIREDF_LICENCE:
// System.out.println ("setStatus: EXPIREDF");
                status = EXPIREDF;
                break;
            case Crypt.CORRUPT_LICENCE:
// System.out.println ("setStatus: CORRUPT");
                status = CORRUPT;
                break;
            case Crypt.OLD_LICENCE:
// System.out.println ("setStatus: DEREGISTERED");
                status = DEREGISTERED;
                break;
            default:
                if (key.hasExpired ())
                {
// System.out.println ("setStatus: EXPIRED");
                    status = EXPIRED;
                }
                else
                {
// System.out.println ("setStatus: EVAL");
                    status = EVAL;
                }
                break;
            }

        }
    }

    void setKey (Key key)
    {
        this.key = key;
        setStatus ();
        modTime = key.modTime;
        timeLast = (int) (System.currentTimeMillis () / 1000);
        timeLeft = key.evalPeriod != 0 ?
            key.evalPeriod * 24 * 60 * 60 :
            (key.expiry + 1) * 24 * 60 * 60 - timeLast;
        if (timeLeft < 0)
            timeLeft = 0;
// System.out.println ("setKey: timeStart " + timeLast + " timeLeft " + timeLeft);
    }

    // Cobble together an expired key - used when the registration
    // data is corrupted (assumed to have been tampered with), and
    // when an evaluation key has expired.

    void buildExpiredKey (Details fd, int status)
    {
        int logId = key == null ? 0 : key.logId;
        if (logId == 0)
            logId = fd.key == null ? 0 : fd.key.logId;
        if (logId == 0)
            logId = 1;
        if (user.equals ("") || company.equals (""))
        {
            user = newUser;
            company = newCompany;
        }
        if (user.equals (""))
            user = "None";
        if (company.equals (""))
            company = "None";
        int expiry = Crypt.EXPIRED_LICENCE;
        switch (status)
        {
        case Details.EXPIRED:
            expiry = Crypt.EXPIRED_LICENCE;
            break;
        case Details.EXPIREDF:
            expiry = Crypt.EXPIREDF_LICENCE;
            break;
        case Details.CORRUPT:
            expiry = Crypt.CORRUPT_LICENCE;
            break;
        case Details.DEREGISTERED:
            expiry = Crypt.OLD_LICENCE;
            break;
        }
        key = new Key (product, majorVersion, modTime,
            Crypt.hash (user + company),
            expiry, logId, 0, random, 0, 0, Crypt.CHAR_VERSION_CURRENT);
        this.status = status;
    }

    // Build a "new" key - used in the "funny" file before the user has
    // entered a proper registration key, so that we can store the 'random'
    // value.

    void buildNewKey ()
    {
        if (user.equals ("") || company.equals (""))
        {
            user = newUser;
            company = newCompany;
        }
        key = new Key (product, majorVersion, modTime,
            Crypt.hash (user + company),
            Crypt.NEW_LICENCE, 0, 0, random, 0, 0, Crypt.CHAR_VERSION_CURRENT);
    }

    // Build a "deregistered" key

    void buildOldKey ()
    {
        if (status == DEREGISTERED)
            return;
        key = new Key (product, majorVersion, modTime,
            Crypt.hash (user + company),
            Crypt.OLD_LICENCE, key.logId, 0, random, 0, 0,
            Crypt.CHAR_VERSION_CURRENT);
        status = DEREGISTERED;
    }

    int daysLeft ()
    {
        int daysLeft = 3;
        if (key != null)
        {
// System.out.println ("expiry: " + Registration.formatTime (key.expiry * 24 * 60 * 60));
            daysLeft = key.daysLeft ();
// System.out.println ("key.daysLeft: " + daysLeft);
        }
        if (timeLast != 0)
        {
            int dl = (timeLeft + 24 * 60 * 60 - 1) / (24 * 60 * 60);
// System.out.println ("funny.daysLeft: " + dl);
            if (dl < daysLeft)
                daysLeft = dl;
        }

        return daysLeft;
    }
}

