package org.chenyang.http.impl.entity;

import org.chenyang.http.Header;
import org.chenyang.http.HeaderElement;
import org.chenyang.http.HttpException;
import org.chenyang.http.HttpMessage;
import org.chenyang.http.ParseException;
import org.chenyang.http.ProtocolException;
import org.chenyang.http.annotation.Contract;
import org.chenyang.http.annotation.ThreadingBehavior;
import org.chenyang.http.entity.ContentLengthStrategy;
import org.chenyang.http.protocol.HTTP;
import org.chenyang.http.util.Args;

@Contract(
   threading = ThreadingBehavior.IMMUTABLE
)
public class LaxContentLengthStrategy implements ContentLengthStrategy {
   public static final LaxContentLengthStrategy INSTANCE = new LaxContentLengthStrategy();
   private final int implicitLen;

   public LaxContentLengthStrategy(int implicitLen) {
      this.implicitLen = implicitLen;
   }

   public LaxContentLengthStrategy() {
      this(-1);
   }

    @Override
    public long determineLength(final HttpMessage message) throws HttpException {
        Args.notNull(message, "HTTP message");

        final Header transferEncodingHeader = message.getFirstHeader(HTTP.TRANSFER_ENCODING);
        // We use Transfer-Encoding if present and ignore Content-Length.
        // RFC2616, 4.4 item number 3
        if (transferEncodingHeader != null) {
            final HeaderElement[] encodings;
            try {
                encodings = transferEncodingHeader.getElements();
            } catch (final ParseException px) {
                throw new ProtocolException
                        ("Invalid Transfer-Encoding header value: " +
                                transferEncodingHeader, px);
            }
            // The chunked encoding must be the last one applied RFC2616, 14.41
            final int len = encodings.length;
            if (HTTP.IDENTITY_CODING.equalsIgnoreCase(transferEncodingHeader.getValue())) {
                return IDENTITY;
            } else if ((len > 0) && (HTTP.CHUNK_CODING.equalsIgnoreCase(
                    encodings[len - 1].getName()))) {
                return CHUNKED;
            } else {
                return IDENTITY;
            }
        }
        final Header contentLengthHeader = message.getFirstHeader(HTTP.CONTENT_LEN);
        if (contentLengthHeader != null) {
            long contentLen = -1;
            final Header[] headers = message.getHeaders(HTTP.CONTENT_LEN);
            for (int i = headers.length - 1; i >= 0; i--) {
                final Header header = headers[i];
                try {
                    contentLen = Long.parseLong(header.getValue());
                    break;
                } catch (final NumberFormatException ignore) {
                }
                // See if we can have better luck with another header, if present
            }
            return contentLen >= 0 ? contentLen : IDENTITY;
        }
        return this.implicitLen;
    }
}
