package com.philisense.www.license;

import javax.swing.filechooser.*;
import javax.swing.filechooser.FileFilter;

import com.philisense.www.utils.*;
import com.philisense.www.xml.*;

import java.util.*;
import javax.security.auth.x500.*;
import java.util.prefs.*;
import java.io.*;

public class LicenseManager implements LicenseCreator, LicenseVerifier
{
    private static final long TIMEOUT = 1800000L;
    private static final String PREFERENCES_KEY;
    public static final String LICENSE_SUFFIX;
    protected static final String CN;
    private static final String CN_USER;
    private static final String USER;
    private static final String SYSTEM;
    private static final String EXC_INVALID_SUBJECT;
    private static final String EXC_HOLDER_IS_NULL;
    private static final String EXC_ISSUER_IS_NULL;
    private static final String EXC_ISSUED_IS_NULL;
    private static final String EXC_LICENSE_IS_NOT_YET_VALID;
    private static final String EXC_LICENSE_HAS_EXPIRED;
    private static final String EXC_CONSUMER_TYPE_IS_NULL;
    private static final String EXC_CONSUMER_TYPE_IS_NOT_USER;
    private static final String EXC_CONSUMER_AMOUNT_IS_NOT_ONE;
    private static final String EXC_CONSUMER_AMOUNT_IS_NOT_POSITIVE;
    private static final String FILE_FILTER_DESCRIPTION;
    private static final String FILE_FILTER_SUFFIX;
    private LicenseParam param;
    private LicenseNotary notary;
    private PrivacyGuard guard;
    private GenericCertificate certificate;
    private long certificateTimeout;
    private FileFilter fileFilter;
    
    protected static Date midnight() {
        final Calendar instance = Calendar.getInstance();
        instance.set(11, 0);
        instance.set(12, 0);
        instance.set(13, 0);
        instance.set(14, 0);
        return instance.getTime();
    }
    
    protected LicenseManager() {
    }
    
    public LicenseManager(final LicenseParam licenseParam0) {
        this.setLicenseParam0(licenseParam0);
    }
    
    public synchronized LicenseParam getLicenseParam() {
        return this.param;
    }
    
    public synchronized void setLicenseParam(final LicenseParam licenseParam0) {
        this.setLicenseParam0(licenseParam0);
    }
    
    private void setLicenseParam0(final LicenseParam param) {
        final CipherParam cipherParam;
        if (null == param.getSubject() || null == param.getKeyStoreParam() || null == (cipherParam = param.getCipherParam())) {
            throw new NullPointerException();
        }
        Policy.getCurrent().checkPwd(cipherParam.getKeyPwd());
        this.param = param;
        this.notary = null;
        this.setCertificate0(null);
        this.fileFilter = null;
    }
    
    public final synchronized void store(final LicenseContent licenseContent, final File file) throws Exception {
        this.store(licenseContent, this.getLicenseNotary(), file);
    }
    
    protected synchronized void store(final LicenseContent licenseContent, final LicenseNotary licenseNotary, final File file) throws Exception {
        storeLicenseKey(this.create(licenseContent, licenseNotary), file);
    }
    
    @Override
    public final synchronized byte[] create(final LicenseContent licenseContent) throws Exception {
        return this.create(licenseContent, this.getLicenseNotary());
    }
    
    protected synchronized byte[] create(final LicenseContent licenseContent, final LicenseNotary licenseNotary) throws Exception {
        this.initialize(licenseContent);
        this.validate(licenseContent);
        return this.getPrivacyGuard().cert2key(licenseNotary.sign(licenseContent));
    }
    
    public final synchronized LicenseContent install(final File file) throws Exception {
        return this.install(file, this.getLicenseNotary());
    }
    
    protected synchronized LicenseContent install(final File file, final LicenseNotary licenseNotary) throws Exception {
        return this.install(loadLicenseKey(file), licenseNotary);
    }
    
    protected synchronized LicenseContent install(final byte[] licenseKey, final LicenseNotary licenseNotary) throws Exception {
        final GenericCertificate key2cert = this.getPrivacyGuard().key2cert(licenseKey);
        licenseNotary.verify(key2cert);
        final LicenseContent licenseContent = (LicenseContent)key2cert.getContent();
        this.validate(licenseContent);
        this.setLicenseKey(licenseKey);
        this.setCertificate(key2cert);
        return licenseContent;
    }
    
    public final synchronized LicenseContent verify() throws Exception {
        return this.verify(this.getLicenseNotary());
    }
    
    protected synchronized LicenseContent verify(final LicenseNotary licenseNotary) throws Exception {
        final GenericCertificate certificate = this.getCertificate();
        if (null != certificate) {
            return (LicenseContent)certificate.getContent();
        }
        final byte[] licenseKey = this.getLicenseKey();
        if (null == licenseKey) {
            throw new NoLicenseInstalledException(this.getLicenseParam().getSubject());
        }
        final GenericCertificate key2cert = this.getPrivacyGuard().key2cert(licenseKey);
        licenseNotary.verify(key2cert);
        final LicenseContent licenseContent = (LicenseContent)key2cert.getContent();
        this.validate(licenseContent);
        this.setCertificate(key2cert);
        return licenseContent;
    }
    
    @Override
    public final synchronized LicenseContent verify(final byte[] array) throws Exception {
        return this.verify(array, this.getLicenseNotary());
    }
    
    protected synchronized LicenseContent verify(final byte[] array, final LicenseNotary licenseNotary) throws Exception {
        final GenericCertificate key2cert = this.getPrivacyGuard().key2cert(array);
        licenseNotary.verify(key2cert);
        final LicenseContent licenseContent = (LicenseContent)key2cert.getContent();
        this.validate(licenseContent);
        return licenseContent;
    }
    
    public synchronized void uninstall() throws Exception {
        this.setLicenseKey(null);
        this.setCertificate(null);
    }
    
    protected synchronized void initialize(final LicenseContent licenseContent) {
        if (null == licenseContent.getHolder()) {
            licenseContent.setHolder(new X500Principal(LicenseManager.CN_USER));
        }
        if (null == licenseContent.getSubject()) {
            licenseContent.setSubject(this.getLicenseParam().getSubject());
        }
        if (null == licenseContent.getConsumerType()) {
            final Preferences preferences = this.getLicenseParam().getPreferences();
            if (null != preferences) {
                if (preferences.isUserNode()) {
                    licenseContent.setConsumerType(LicenseManager.USER);
                }
                else {
                    licenseContent.setConsumerType(LicenseManager.SYSTEM);
                }
                licenseContent.setConsumerAmount(1);
            }
        }
        if (null == licenseContent.getIssuer()) {
            licenseContent.setIssuer(new X500Principal(LicenseManager.CN + this.getLicenseParam().getSubject()));
        }
        if (null == licenseContent.getIssued()) {
            licenseContent.setIssued(new Date());
        }
        if (null == licenseContent.getNotBefore()) {
            licenseContent.setNotBefore(midnight());
        }
    }
    
    protected synchronized void validate(final LicenseContent licenseContent) throws LicenseContentException {
        final LicenseParam licenseParam = this.getLicenseParam();
        if (!licenseParam.getSubject().equals(licenseContent.getSubject())) {
            throw new LicenseContentException(LicenseManager.EXC_INVALID_SUBJECT);
        }
        if (null == licenseContent.getHolder()) {
            throw new LicenseContentException(LicenseManager.EXC_HOLDER_IS_NULL);
        }
        if (null == licenseContent.getIssuer()) {
            throw new LicenseContentException(LicenseManager.EXC_ISSUER_IS_NULL);
        }
        if (null == licenseContent.getIssued()) {
            throw new LicenseContentException(LicenseManager.EXC_ISSUED_IS_NULL);
        }
        final Date date = new Date();
        final Date notBefore = licenseContent.getNotBefore();
        if (null != notBefore && date.before(notBefore)) {
            throw new LicenseContentException(LicenseManager.EXC_LICENSE_IS_NOT_YET_VALID);
        }
        final Date notAfter = licenseContent.getNotAfter();
        if (null != notAfter && date.after(notAfter)) {
            throw new LicenseContentException(LicenseManager.EXC_LICENSE_HAS_EXPIRED);
        }
        final String consumerType = licenseContent.getConsumerType();
        if (null == consumerType) {
            throw new LicenseContentException(LicenseManager.EXC_CONSUMER_TYPE_IS_NULL);
        }
        final Preferences preferences = licenseParam.getPreferences();
        if (null != preferences && preferences.isUserNode()) {
            if (!LicenseManager.USER.equalsIgnoreCase(consumerType)) {
                throw new LicenseContentException(LicenseManager.EXC_CONSUMER_TYPE_IS_NOT_USER);
            }
            if (1 != licenseContent.getConsumerAmount()) {
                throw new LicenseContentException(LicenseManager.EXC_CONSUMER_AMOUNT_IS_NOT_ONE);
            }
        }
        else if (0 >= licenseContent.getConsumerAmount()) {
            throw new LicenseContentException(LicenseManager.EXC_CONSUMER_AMOUNT_IS_NOT_POSITIVE);
        }
    }
    
    protected synchronized GenericCertificate getCertificate() {
        return (System.currentTimeMillis() < this.certificateTimeout) ? this.certificate : null;
    }
    
    protected synchronized void setCertificate(final GenericCertificate certificate0) {
        this.setCertificate0(certificate0);
    }
    
    private void setCertificate0(final GenericCertificate certificate) {
        final GenericCertificate genericCertificate = null;
        this.certificate = certificate;
        this.certificateTimeout = ((genericCertificate != certificate) ? (System.currentTimeMillis() + 1800000L) : 0L);
    }
    
    protected synchronized byte[] getLicenseKey() {
        return this.getLicenseParam().getPreferences().getByteArray(LicenseManager.PREFERENCES_KEY, null);
    }
    
    protected synchronized void setLicenseKey(final byte[] array) {
        final Preferences preferences = this.getLicenseParam().getPreferences();
        if (null != array) {
            preferences.putByteArray(LicenseManager.PREFERENCES_KEY, array);
        }
        else {
            preferences.remove(LicenseManager.PREFERENCES_KEY);
        }
    }
    
    protected static void storeLicenseKey(final byte[] array, final File file) throws IOException {
        final FileOutputStream fileOutputStream = new FileOutputStream(file);
        try {
            fileOutputStream.write(array);
        }
        finally {
            try {
                fileOutputStream.close();
            }
            catch (IOException ex) {}
        }
    }
    
    protected static byte[] loadLicenseKey(final File file) throws IOException {
        final byte[] array = new byte[Math.min((int)file.length(), 1048576)];
        final FileInputStream fileInputStream = new FileInputStream(file);
        try {
            fileInputStream.read(array);
        }
        finally {
            fileInputStream.close();
        }
        return array;
    }
    
    protected synchronized LicenseNotary getLicenseNotary() {
        if (null == this.notary) {
            this.notary = new LicenseNotary(this.getLicenseParam().getKeyStoreParam());
        }
        return this.notary;
    }
    
    protected synchronized PrivacyGuard getPrivacyGuard() {
        if (null == this.guard) {
            this.guard = new PrivacyGuard(this.getLicenseParam().getCipherParam());
        }
        return this.guard;
    }
    
    public synchronized FileFilter getFileFilter() {
        if (this.fileFilter != null) {
            return this.fileFilter;
        }
        final String string = Resources.getString(LicenseManager.FILE_FILTER_DESCRIPTION, this.getLicenseParam().getSubject());
        if (File.separatorChar == '\\') {
            this.fileFilter = new FileFilter() {
                @Override
                public boolean accept(final File file) {
                    return file.isDirectory() || file.getPath().toLowerCase().endsWith(LicenseManager.LICENSE_SUFFIX);
                }
                
                @Override
                public String getDescription() {
                    return string + LicenseManager.FILE_FILTER_SUFFIX;
                }
            };
        }
        else {
            this.fileFilter = new FileFilter() {
                @Override
                public boolean accept(final File file) {
                    return file.isDirectory() || file.getPath().endsWith(LicenseManager.LICENSE_SUFFIX);
                }
                
                @Override
                public String getDescription() {
                    return string + LicenseManager.FILE_FILTER_SUFFIX;
                }
            };
        }
        return this.fileFilter;
    }
    
    static {
        PREFERENCES_KEY = new ObfuscatedString(new long[] { -2999492566024573771L, -1728025856628382701L }).toString();
        LICENSE_SUFFIX = new ObfuscatedString(new long[] { -7559156485370438418L, 5084921010819724770L }).toString();
        assert LicenseManager.LICENSE_SUFFIX.equals(LicenseManager.LICENSE_SUFFIX.toLowerCase());
        CN = new ObfuscatedString(new long[] { 7165044359350484836L, -6008675436704023088L }).toString();
        CN_USER = LicenseManager.CN + Resources.getString(new ObfuscatedString(new long[] { -883182015789302099L, 6587252612286394632L }).toString());
        USER = new ObfuscatedString(new long[] { -6950934198262740461L, -10280221617836935L }).toString();
        SYSTEM = new ObfuscatedString(new long[] { -1441033263392531498L, 6113162389128247115L }).toString();
        EXC_INVALID_SUBJECT = new ObfuscatedString(new long[] { -9211605111142713620L, 391714365510707393L, -7356761750428556372L, 6379560902598103028L }).toString();
        EXC_HOLDER_IS_NULL = new ObfuscatedString(new long[] { 7150026245468079143L, 6314884536402738366L, -1360923923476698800L }).toString();
        EXC_ISSUER_IS_NULL = new ObfuscatedString(new long[] { -3034693013076752554L, -1011266899694033610L, 6775785917404597234L }).toString();
        EXC_ISSUED_IS_NULL = new ObfuscatedString(new long[] { -6084371209004858580L, 3028840747031697166L, -3524637886726219307L }).toString();
        EXC_LICENSE_IS_NOT_YET_VALID = new ObfuscatedString(new long[] { 5434633639502011825L, -3406117476263181371L, 6903673940810780388L, -6816911225052310716L }).toString();
        EXC_LICENSE_HAS_EXPIRED = new ObfuscatedString(new long[] { 1000558500458715757L, -6998261911041258483L, -5490039629745846648L, 3561172928787106880L }).toString();
        EXC_CONSUMER_TYPE_IS_NULL = new ObfuscatedString(new long[] { -3274088377466921882L, -1704115158449736962L, -1134622897105293263L, 2875630655915253859L }).toString();
        EXC_CONSUMER_TYPE_IS_NOT_USER = new ObfuscatedString(new long[] { -3559580260061340089L, 8807812719464926891L, 3255622466169980128L, 3208430498260873670L, 8772089725159421213L }).toString();
        EXC_CONSUMER_AMOUNT_IS_NOT_ONE = new ObfuscatedString(new long[] { 6854702630454082314L, -1676630527348424687L, 4853969635229547239L, -7087814313396201500L, 7133601245775504376L }).toString();
        EXC_CONSUMER_AMOUNT_IS_NOT_POSITIVE = new ObfuscatedString(new long[] { -5670394608177286583L, -3674104453170648872L, 4159301984262248157L, 7442355638167795990L, 4780252201915657674L }).toString();
        FILE_FILTER_DESCRIPTION = new ObfuscatedString(new long[] { 3160933239845492228L, -2320904495012387647L, -5935185636215549881L, -3418607682842311949L }).toString();
        FILE_FILTER_SUFFIX = new ObfuscatedString(new long[] { -6576160320308571504L, 7010427383913371869L }).toString();
    }
}
