/*
 * Copyright (C) 2005-2006 Kouji Sugisawa. All rights reserved.
 */

package jp.sourceforge.livez.vodafone;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;

import javax.servlet.http.HttpServletRequest;

import jp.sourceforge.livez.IllegalRequestException;

import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;
import org.apache.commons.lang.math.NumberUtils;

/**
 * MS-Profile を実装したクラスです。
 * 
 * @author 杉澤 浩二
 */
final class VodafoneRequestImpl implements VodafoneRequest {

	/**
	 * コンストラクタです。
	 * 
	 * @param request HTTP リクエスト情報
	 * @throws IllegalRequestException
	 */
	public VodafoneRequestImpl(final HttpServletRequest request) throws IllegalRequestException {
		this.userAgent = new VodafoneUserAgentImpl(request);

		String[] strings;
		String s;

		// 端末ID、端末名を取得します
		s = request.getHeader("x-jphone-msname");
		if (s == null)
			throw new IllegalRequestException();
		this.id = s;
		strings = s.split("_");
		this.name = strings[0];

		// カラー対応状況、色数を取得します
		s = request.getHeader("x-jphone-color");
		if (s == null || !s.matches("^(C|G)[0-9]+$"))
			throw new IllegalRequestException();
		this.monochrome = s.charAt(0) == 'G';
		this.color = NumberUtils.toInt(s.substring(1));

		// x-jphone-display ヘッダを処理します
		s = request.getHeader("x-jphone-display");
		if (s == null || !s.matches("^[0-9]{2,}\\*[0-9]{2,}$"))
			throw new IllegalRequestException();
		strings = s.split("\\*");
		this.displayWidth = NumberUtils.toInt(strings[0]);
		this.displayHeight = NumberUtils.toInt(strings[1]);

		// GEOコードを取得します
		s = request.getHeader("x-jphone-geocode");
		if (s != null) {
			try {
				this.geoCode = URLDecoder.decode(s, "Windows-31J");
			} catch (UnsupportedEncodingException e) {
				throw new RuntimeException(e.getMessage(), e);
			}
		}

		s = request.getHeader("x-jphone-java");
		// C4型以外の端末ではこのプロパティを持っていません。
		if (s != null)
			this.jsclVersion = s;

		s = request.getHeader("x-jphone-region");
		if (s != null)
			this.region = s;

		// パケット対応端末で拡張されたMSプロファイル変数
		s = request.getHeader("x-jphone-smaf");
		// 一部の端末ではこのプロパティを持っていません。
		if (s != null)
			this.smaf = new VodafoneSmafImpl(s);

		s = request.getHeader("x-jphone-sound");
		// 新しい端末ではこのプロパティを持っていません。
		if (s != null)
			this.smd = NumberUtils.toInt(s);

		// 端末型を設定します
		this.type = getMSType();

		parseName(this.name, this.type.getCategoryType());

		// CP用情報を設定します
		s = request.getHeader("x-jphone-notifyId");
		if (s != null && !s.matches("^[0-9a-zA-Z]{10}$"))
			throw new IllegalRequestException();
		this.notifyId = s;

		s = request.getHeader("x-jphone-registerd");
		if (!(s == null || s.length() == 0))
			throw new IllegalRequestException();
		this.registerd = s != null;

		this.uid = new UIDImpl(request);

		// 利便性の為の情報を設定します
	}

	private VodafoneType getMSType() {
		final float g = this.userAgent.getGenerationVersion();
		String jscl;

		if (this.userAgent.getVappli() != null)
			jscl = this.userAgent.getVappli().getJsclVersion();
		else
			jscl = null;

		if (g >= 5.0) {
			// 5.0 世代の場合は Ｗ型
			return VodafoneType.W;
		} else if (g >= 4.3) {
			if (jscl != null) {
				if (jscl.compareTo("1.3.2") >= 0)
					return VodafoneType.P7;
				else if (jscl.compareTo("1.2.2") >= 0)
					return VodafoneType.P6;
			}
			return VodafoneType.UNKNOWN_P;
		} else if (g >= 4.2) {
			if (jscl != null) {
				if (jscl.compareTo("1.2") >= 0)
					return VodafoneType.P5;
				return VodafoneType.P4_2;
			}
			return VodafoneType.UNKNOWN_P;
		} else if (g >= 4.0) {
			return VodafoneType.P4_1;
		} else if (g >= 3.0) {
			if (this.jsclVersion != null && this.jsclVersion.equals("1.0"))
				return VodafoneType.C4;
			return VodafoneType.C3;
		} else if (g >= 2.0) {
			return VodafoneType.C2;
		} else if (g >= 1.0) {
			if (jscl != null)
				return VodafoneType.X_3GC;
			return VodafoneType.S_3GC;
		}

		return VodafoneType.UNKNOWN;
	}

	/**
	 * 機種名からベンダーID及びシリーズ名を設定します。<p>
	 * 
	 * @param name 端末名
	 * @param category 端末の型
	 */
	private void parseName(final String name, final VodafoneCategoryType category) {
		if (name == null) 
			return;

		if (name.matches("^" + VodafoneUserAgentImpl.JPHONE_MSNAME + "$")) {
			final int start = "J-".length();

			// ベンダーIDを処理します
			int end = -1;
			for (int i = start; i < name.length(); i++) {
				final char c = name.charAt(i);
				// 数字が現れたら処理を抜けます
				if (c >= '0' && c <= '9') {
					end = i;
					break;
				}
			}

			if (end != -1)
				this.vendorId = name.substring(start, end);

			// シリーズ名を処理します
		    // 端末名が J-PHONE 時代の物の場合
			for (int i = end + 1; i < name.length(); i++) {
				final char c = name.charAt(i);
				// 数字でなくなったら処理を抜けます
				if (c < '0' || c > '9') {
					this.seriesName = "J-" + name.substring(end + 1, i);
					break;
				}
			}
		} else  if (name.matches("^" + VodafoneUserAgentImpl.VODAFONE_MSNAME + "$")) {
		    // 端末名が Vodafone 時代の物の場合

			// ベンダーIDを取得します
			for (int i = 4; i < name.length(); i++) {
				final char c = name.charAt(i);
				if (c < 'A' || c > 'Z') {
					this.vendorId = name.substring(5, i);
					break;
				}
			}

			// シリーズ名を取得します
			if (category == VodafoneCategoryType.THREE_GC)
				this.seriesName = "3G";
			else
				this.seriesName = "V" + name.charAt(1);
		}
	}

	/**
	 * ユーザエージェント情報を保持します。
	 */
	private VodafoneUserAgent userAgent = null;

	/*
	 * @see jp.sourceforge.livez.MobilePhoneRequest#getUserAgent()
	 */
	public VodafoneUserAgent getUserAgent() {
		return userAgent;
	}

	private VodafoneType type = VodafoneType.UNKNOWN;

	/*
	 * @see jp.sourceforge.livez.vodafone.VodafoneRequest#getType()
	 */
	public VodafoneType getType() {
		return type;
	}

	private String id = null;

	/*
	 * @see jp.sourceforge.livez.vodafone.VodafoneRequest#getId()
	 */
	public String getId() {
		return id;
	}

	private String name = null;

	/*
	 * @see jp.sourceforge.livez.vodafone.VodafoneRequest#getName()
	 */
	public String getName() {
		return name;
	}

	/**
	 * シリーズ名を保持します。
	 */
	private String seriesName = null;

	/*
	 * @see jp.sourceforge.livez.vodafone.VodafoneRequest#getSeriesName()
	 */
	public String getSeriesName() {
		return seriesName;
	}

	/**
	 * ベンダーIDを保持します。
	 */
	private String vendorId = null;

	/*
	 * @see jp.sourceforge.livez.vodafone.VodafoneRequest#getVendorId()
	 */
	public String getVendorId() {
		return vendorId;
	}

	private boolean monochrome = false;

	/*
	 * @see jp.sourceforge.livez.vodafone.VodafoneRequest#isMonochrome()
	 */
	public boolean isMonochrome() {
		return monochrome;
	}

	private int color = 0;

	/*
	 * @see jp.sourceforge.livez.vodafone.VodafoneRequest#getColor()
	 */
	public int getColor() {
		return color;
	}

	private int displayWidth = 0;

	/*
	 * @see jp.sourceforge.livez.vodafone.VodafoneRequest#getDisplayWidth()
	 */
	public int getDisplayWidth() {
		return displayWidth;
	}

	private int displayHeight = 0;

	/*
	 * @see jp.sourceforge.livez.vodafone.VodafoneRequest#getDisplayHeight()
	 */
	public int getDisplayHeight() {
		return displayHeight;
	}

	/**
	 * 位置情報を保持します。
	 */
	private String geoCode = null;

	/*
	 * @see jp.sourceforge.livez.vodafone.VodafoneRequest#getGeoCode()
	 */
	public String getGeoCode() {
		return geoCode;
	}

	/**
	 * 対応 Java (JSCL) バージョン情報を保持します。
	 */
	private String jsclVersion = null;

	/*
	 * @see jp.sourceforge.livez.vodafone.VodafoneRequest#getJsclVersion()
	 */
	public String getJsclVersion() {
		return jsclVersion;
	}

	/**
	 * ユーザの利用地域を保持します。
	 */
	private String region = null;

	/*
	 * @see jp.sourceforge.livez.vodafone.VodafoneRequest#getRegion()
	 */
	public String getRegion() {
		return region;
	}

	/**
	 * 対応 SMAF 情報を保持します。
	 */
	private VodafoneSmaf smaf = null;

	/*
	 * @see jp.sourceforge.livez.vodafone.VodafoneRequest#getSmaf()
	 */
	public VodafoneSmaf getSmaf() {
		return smaf;
	}

	/**
	 * 演奏可能な SMD の最大和音数を保持します。
	 */
	private int smd = 0;

	/*
	 * @see jp.sourceforge.livez.vodafone.VodafoneRequest#getSmd()
	 */
	public int getSmd() {
		return smd;
	}

	private String notifyId = null;

	/*
	 * @see jp.sourceforge.livez.vodafone.VodafoneRequest#getNotifyId()
	 */
	public String getNotifyId() {
		return notifyId;
	}

	private boolean registerd = false;

	/*
	 * @see jp.sourceforge.livez.vodafone.VodafoneRequest#getRegisterd()
	 */
	public boolean getRegisterd() {
		return registerd;
	}

	/**
	 * UID 情報を保持します。
	 */
	private UID uid = null;

	/*
	 * @see jp.sourceforge.livez.vodafone.VodafoneRequest#getUID()
	 */
	public UID getUID() {
		return uid;
	}

	@Override
	public int hashCode() {
		return HashCodeBuilder.reflectionHashCode(this);
	}

	@Override
	public boolean equals(final Object obj) {
		return EqualsBuilder.reflectionEquals(this, obj);
	}

	@Override
	public String toString() {
		return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE);
	}

}
