/*
 * @(#)HttpCookie.java
 *
 * Copyright (c) 2001-2002 JScape
 * 1147 S. 53rd Pl., Mesa, Arizona, 85206, U.S.A.
 * All rights reserved.
 *
 * This software is the confidential and proprietary information of
 * JScape. ("Confidential Information").  You shall not disclose such
 * Confidential Information and shall use it only in accordance with
 * the terms of the license agreement you entered into with JScape.
 */

package com.jscape.inet.http;

import com.jscape.inet.mime.MimeException;
import com.jscape.inet.mime.MimeHeader;
import com.jscape.util.ByteBuffer;

import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.StringTokenizer;
import java.util.TimeZone;

/**
 * Represents an HTTP cookie returned in an HTTP response.
 *
 * @see HttpResponse#getCookies
 */
public class HttpCookie
        extends MimeHeader {

    private static final String[] EXPIRATION_DATE_FORMATS = { "EEE, dd-MMM-yyyy HH:mm:ss z", "EEE, dd MMM yyyy HH:mm:ss z" };

    private String cookieName;
    private String cookieValue;
    private String path;
    private boolean secure;
    private String domain;
    private Date expires;
    private String comment;

    /**
     * Constructs a new <code>HttpCookie</code> instance.
     *
     * @throws MimeException if a MIME related parsing error occurs
     */
    public HttpCookie()
            throws MimeException {
        super("", "", "us-ascii");
        setFolding(false);
    }

    /**
     * Gets the name of this <code>HttpCookie</code>
     *
     * @return cookie name
     */
    public String getCookieName() {
        return cookieName;
    }

    /**
     * Gets the value of this <code>HttpCookie</code>
     *
     * @return cookie value
     */
    public String getCookieValue() {
        return cookieValue;
    }

    /**
     * Gets the path of this <code>HttpCookie</code>
     *
     * @return path
     */
    public String getPath() {
        return path;
    }

    /**
     * Gets the comment of this <code>HttpCookie</code>
     *
     * @return comment
     */
    public String getComment() {
        return comment;
    }

    /**
     * Gets the expiration date of this <code>HttpCookie</code>
     *
     * @return expires
     */
    public Date getExpires() {
        return expires;
    }

    /**
     * Gets the domain of this <code>HttpCookie</code>
     *
     * @return domain
     */
    public String getDomain() {
        return domain;
    }

    /**
     * Checks if this <code>HttpCookie</code> is secure.
     *
     * @return <code>true</code> if cookie is secure, <code>false</code>
     *         otherwise
     */
    public boolean isSecure() {
        return secure;
    }

    /**
     * Parses information contained in MIME header.
     *
     * @param data the MIME header
     *
     * @throws MimeException if an error occurs while parsing
     */
    protected void parse(byte[] data)
            throws MimeException {
        ByteBuffer buffer = new ByteBuffer(data);
        parse(buffer);
    }

    /**
     * Parses information contained in MIME header.
     *
     * @param buffer the MIME header
     *
     * @throws MimeException if an error occurs while parsing
     */
    protected void parse(ByteBuffer buffer)
            throws MimeException {
        buffer = new ByteBuffer(unfold(buffer.toString()).getBytes(StandardCharsets.US_ASCII));
        super.parse(buffer);
        StringTokenizer tokenizer = new StringTokenizer(getValue(), ";");
        boolean first = true;

        while (tokenizer.hasMoreTokens()) {
            String token = tokenizer.nextToken();
            parseCookieAttribute(token, first);
            first = false;
        }
    }

    /**
     * Parses individual attributes of cookie.
     *
     * @param value the attribute
     * @param first <code>true</code> if first attribute in cookie
     */
    private void parseCookieAttribute(String value, boolean first) {
        StringTokenizer attribute = new StringTokenizer(value, "=");

        if (attribute.countTokens() == 2) {
            String attributeName = attribute.nextToken().trim();
            String attributeValue = attribute.nextToken().trim();

            if (first) {
                this.cookieName = attributeName;
                this.cookieValue = attributeValue;
            } else {
                setCookieParameter(attributeName, attributeValue);
            }
        } else if (attribute.countTokens() == 1) {
            String attributeName = attribute.nextToken().trim();
            if (first) {
                this.cookieName = attributeName;
                this.cookieValue = "";
            } else {
                if (attributeName.equalsIgnoreCase("Secure")) {
                    this.secure = true;
                }
            }
        } else {
            String attributeName = attribute.nextToken();

            try {
                String val = value.substring(value.indexOf("=") + 1);

                if (first) {
                    this.cookieName = attributeName;
                    this.cookieValue = val;
                } else {
                    setCookieParameter(attributeName, val);
                }
            } catch (IndexOutOfBoundsException iob) {
                // todo: ignore?
            }
        }
    }

    /**
     * Evaluates attributes of Set-Cookie
     *
     * @param name  the attribute name
     * @param value the attribute value
     */
    private void setCookieParameter(String name, String value) {
        if (name.equalsIgnoreCase("Path")) {
            this.path = value;
        } else if (name.equalsIgnoreCase("Domain")) {
            this.domain = value;
        } else if (name.equalsIgnoreCase("Comment")) {
            this.comment = value;
        } else if (name.equalsIgnoreCase("Expires")) {
            initExpires(value);
        }
    }

    private void initExpires(String value) {
        for (int i = 0; i < EXPIRATION_DATE_FORMATS.length; ++i) {
            SimpleDateFormat format = new SimpleDateFormat(EXPIRATION_DATE_FORMATS[i]);
            format.setTimeZone(TimeZone.getTimeZone("GMT"));
            try {
                this.expires = format.parse(value);
                return;
            } catch (ParseException e) {
                // ignore
            }
        }
    }

}
