/**
 * class to hold the table data about an Application.
 */
package com.mindprod.vercheck;

import com.mindprod.common11.BigDate;
import static com.mindprod.vercheck.AppState.*;

import javax.swing.Icon;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URL;

/**
 * represents one row of cells, all four columns.
 */
public class
        AppToWatch implements Serializable, Comparable<AppToWatch>
    {
    // ------------------------------ FIELDS ------------------------------

    /**
     * how many days we keep status before before we consider them stale, 0= always stale 1= yesterday is stale
     */
    private static final int STALE_DAYS = 2;

    /**
     * Defining a layout version for a class.
     */
    public static final long serialVersionUID = 7L;

    /**
     * date we last checked the statuses by probing websites
     */
    static BigDate dateChecked;

    /**
     * today, using tho local timezone to figure out when midnight is.
     */
    static final BigDate localToday = BigDate.localToday();

    /**
     * has this appName changed recently, and other states
     */
    private AppState state;

    /**
     * date this version was released, null if unknown
     */
    private BigDate dateReleased;

    /**
     * name of the app
     */
    private String appName;

    /**
     * what this app does
     */
    private String description;
    /**
     * String you will find in the web page if the version has not changed.
     */
    private String marker;

    /**
     * current version of the appName
     */
    private String version;

    /**
     * url of the web page to check for a version change.
     */
    private URL versionURL;

    // -------------------------- PUBLIC INSTANCE  METHODS --------------------------
    /**
     * no-arg constructor
     */
    public AppToWatch()
        {
        this.state = AppState.EMPTY;
        this.appName = "";
        this.version = "";
        this.description = "";
        this.downloadURL = "";
        this.versionURL = null;
        this.marker = "";
        this.dateReleased = null;
        }

    /**
     * constructor
     *
     * @param appName      name of the appName
     * @param version      version of the appName
     * @param description  what the app is for
     * @param downloadURL  where to get info on downloading.
     * @param versionURL   url to check the appName
     * @param marker       string expected to find at url if version has not changed.
     * @param dateReleased date this version was released
     */
    public AppToWatch(
            String appName,
            String version,
            String description,
            String downloadURL,
            String versionURL,
            String marker,
            BigDate dateReleased )
        {
        this.state = AppState.UNKNOWN;
        this.appName = appName;
        this.version = version;
        this.description = description;
        this.downloadURL = downloadURL;
        setVersionURL( versionURL );
        this.marker = marker;
        this.dateReleased = dateReleased;
        }

    /**
     * compare this object with another to sort in ascending alpha order
     *
     * @param o other AppToWatch to compare
     */
    public int compareTo( AppToWatch o )
        {
        return this.appName.compareToIgnoreCase( o.appName );
        }

    /**
     * get URL where can find out about how to download, usually relative URL on mindprod.com, not
     * intended for general use.
     *
     * @return url for download info
     */
    public String getDownloadURL()
        {
        return downloadURL;
        }

    /**
     * where to get info on how to download.  Not displayed in VerCheck. Exported.
     */
    private String downloadURL;

    /**
     * get  name of the appName
     *
     * @return name of the appName
     */
    public String getAppName()
        {
        return appName;
        }

    public BigDate getDateReleased()
        {
        return dateReleased;
        }

    /**
     * set the description of what app does
     *
     * @param description what the app does
     */
    public void setDescription( String description )
        {
        this.description = description;
        // does not affect state
        }

    /**
     * get description of what app is for
     *
     * @return description of this app, what it is for.
     */
    public String getDescription()
        {
        return description;
        }

    /**
     * get icon for the row
     *
     * @return icon to represent the row
     */
    public Icon getIcon()
        {
        return state.getIcon();
        }

    /**
     * get marker string expected to find at url if version has not changed.
     *
     * @return marker string expected to find at url if version has not changed.
     */
    public String getMarker()
        {
        return marker;
        }

    /**
     * get enum AppState of appName
     *
     * @return enum AppState of appName
     */
    public AppState getState()
        {
        return state;
        }

    /**
     * get  url to check the appName
     *
     * @return url to check the appName
     */
    public URL getVersionURL()
        {
        return versionURL;
        }

    /**
     * get version of the appName
     *
     * @return version of the appName
     */
    public String getVersion()
        {
        return version;
        }

    /**
     * set  name of the appName
     *
     * @param appName name of the appName
     */
    public void setAppName( String appName )
        {
        if ( appName.equals( this.appName ) )
            {
            return;
            }
        this.appName = appName;
        setState( AppState.UNKNOWN );
        }

    public void setDateReleased( BigDate dateReleased )
        {
        if ( dateReleased != null )
            {
            final int ordinalToday = localToday.getOrdinal();
            final int ordinalReleased = dateReleased.getOrdinal();
            if ( ordinalReleased > ordinalToday )
                {
                Audio.FUTURE_DATE.play();
                return;
                }
            }
        if ( dateReleased.equals( this.dateReleased ) )
            {
            return;
            }
        this.dateReleased = dateReleased;
        setState( AppState.UNKNOWN );
        }

    /**
     * set marker string expected to find at url if version has not changed.
     *
     * @param marker string expected to find at url if version has not changed.
     */

    public void setMarker( String marker )
        {
        if ( marker.equals( this.marker ) )
            {
            return;
            }
        this.marker = marker;
        setState( AppState.UNKNOWN );
        }

    /**
     * set enum AppState of appName
     *
     * @param state enum AppState of appName
     */
    public void setState( AppState state )
        {
        this.state = state;
        normaliseState();
        }

    /**
     * set url to check the appName
     *
     * @param versionURL url to check the appName
     */
    public void setVersionURL( URL versionURL )
        {
        if ( versionURL.equals( this.versionURL ) )
            {
            return;
            }
        this.versionURL = versionURL;
        setState( AppState.UNKNOWN );
        }

    /**
     * set version of the appName
     *
     * @param version version of the appName
     */
    public void setVersion( String version )
        {
        if ( version.equals( this.version ) )
            {
            return;
            }
        this.version = version;
        setState( AppState.UNKNOWN );
        }

    // -------------------------- OTHER METHODS --------------------------

    /**
     * adjust state based on current date and dateReleased
     */
    void normaliseState()
        {
        switch ( state )
            {
            case EMPTY:
            case UNKNOWN:
            case CHECKING:
            case CHANGED:
            case BROKENLINK:
            case INVALID:
                return;

            case UNCHANGED_RELEASED_IN_LAST_WEEK:
            case UNCHANGED_RELEASED_IN_LAST_MONTH:
            case UNCHANGED_RELEASED_MORE_THAN_A_MONTH_AGO:
            case UNCHANGED_RELEASED_MORE_THAN_A_YEAR_AGO:

                if ( dateChecked == null || dateChecked.getOrdinal() == BigDate.NULL_ORDINAL
                     || localToday.getOrdinal() >= dateChecked.getOrdinal() + STALE_DAYS )
                    {
                    state = UNKNOWN;
                    }
                else if ( dateReleased == null || dateReleased.getOrdinal() == BigDate.NULL_ORDINAL )
                    {
                    state = UNCHANGED_RELEASED_MORE_THAN_A_YEAR_AGO;
                    }
                else
                    {
                    final int ageInDays = Math.max( 0, localToday.getOrdinal() - dateReleased.getOrdinal() );
                    if ( ageInDays <= 7 )
                        {
                        state = UNCHANGED_RELEASED_IN_LAST_WEEK;
                        }
                    else if ( ageInDays <= 30 )
                        {
                        state = UNCHANGED_RELEASED_IN_LAST_MONTH;
                        }
                    else if ( ageInDays <= 365 )
                        {
                        state = UNCHANGED_RELEASED_MORE_THAN_A_MONTH_AGO;
                        }
                    else
                        {
                        state = UNCHANGED_RELEASED_MORE_THAN_A_YEAR_AGO;
                        }
                    }
                break;
            default:
                throw new IllegalArgumentException( "Program bug: illegal AppState" );
            }
        }

    /**
     * set url to check the appName
     *
     * @param url url to check the appName
     */
    private void setVersionURL( String url )
        {
        try
            {
            this.versionURL = new URL( url );
            }
        catch ( MalformedURLException e )
            {
            System.err.println( "Malformed Url: " + url );
            this.versionURL = null;
            }
        }
    }