/*
 *  Copyright (C) 2006  Takashi Kasuya <kasuya@sfc.keio.ac.jp>
 *
 * This library is free software; you can redistribute it and/or
 *　modify it under the terms of the GNU Lesser General Public
 *　License as published by the Free Software Foundation; either
 *　version 2.1 of the License, or (at your option) any later version.
 *　This library is distributed in the hope that it will be useful,
 *　but WITHOUT ANY WARRANTY; without even the implied warranty of
 *　MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *　Lesser General Public License for more details.
 *
 *　You should have received a copy of the GNU Lesser General Public
 *　License along with this library; if not, write to the Free Software
 *　Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package jp.ac.naka.ec.sip;

import java.io.UnsupportedEncodingException;

import javax.sdp.SdpFactory;
import javax.sdp.SdpParseException;
import javax.sdp.SessionDescription;
import javax.sip.ClientTransaction;
import javax.sip.ResponseEvent;
import javax.sip.SipException;
import javax.sip.address.SipURI;
import javax.sip.header.CSeqHeader;
import javax.sip.header.FromHeader;
import javax.sip.header.ToHeader;
import javax.sip.message.Request;
import javax.sip.message.Response;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import jp.ac.naka.ec.EventDispatcher;
import jp.ac.naka.ec.entity.AbstractEntity;
import jp.ac.naka.ec.entity.Entity;
import jp.ac.naka.ec.entity.EntityContainer;
import jp.ac.naka.ec.entity.EntityContainerImpl;
import jp.ac.naka.ec.entity.EntityEvent;
import jp.ac.naka.ec.entity.EntityImpl;
import jp.ac.naka.ec.entity.EntityType;
import jp.ac.naka.ec.entity.EntityEvent.EventType;

/**
 * 
 * @author Takashi Kasuya
 * 
 */
public class ResponseDelegate {

	private EntityContainer container;

	private SipCore core;

	private static SdpFactory factory = SdpFactory.getInstance();
	private static Log logger = LogFactory.getLog(ResponseDelegate.class);
	
	public ResponseDelegate() {
		core = SipCore.getInstance();
		container = EntityContainerImpl.getInstance();
	}

	/**
	 * 
	 * @param evt
	 * @return
	 */
	public boolean forward(ResponseEvent evt) {
		Response response = (Response) evt.getResponse();
		StatusCode code = getStatusCode(response.getStatusCode());
		boolean a = false;
		switch (code) {
		case CODE1xx:
			// Trying

			break;
		case CODE2xx:
			// SuccessResponse
			doSuccessResponse(evt);
			a = true;
			break;
		case CODE3xx:
			break;
		case CODE4xx:
			// TODO 認証が必要な場合
			doErrorResponse(evt);
			break;
		case CODE5xx:
			doErrorResponse(evt);
			a = true;
			break;
		case CODE6xx:
		default:
			assert false;
		}
		return a;
	}

	/**
	 * 
	 * @param evt
	 */
	public void doSuccessResponse(ResponseEvent evt) {
		Response res = evt.getResponse();
		try {
			CSeqHeader cseq = (CSeqHeader) res.getHeader(CSeqHeader.NAME);
			// 200 OK
			if (res.getStatusCode() == Response.OK) {
				if (cseq.getMethod().equals(Request.INVITE)) 
					doInviteResponse(res);
				else if (cseq.getMethod().equals(Request.NOTIFY)) 
					doNotifyResponse(res);
				else if (cseq.getMethod().equals(Request.SUBSCRIBE)) 
					doSubscribeResponse(res);
			}
		} catch (Exception ex) {
			ex.printStackTrace();
		}

	}

	private void doInviteResponse(Response res) throws UnsupportedEncodingException, SdpParseException {
		FromHeader from = (FromHeader) res.getHeader(FromHeader.NAME);
		ToHeader to = (ToHeader) res.getHeader(ToHeader.NAME);
		SipURI target_uri = (SipURI) from.getAddress().getURI();
		SipURI source_uri = (SipURI) to.getAddress().getURI();

		Entity source = new EntityImpl(source_uri);
		Entity target = container.getEntity(target_uri.toString());

		// ACK送信
		core.sendAck(source);
		
		SessionDescription sdp = null;
		byte[] raw = res.getRawContent();
		if (raw != null) {
			String sdp_str = new String(raw, "UTF-8");
			sdp = factory.createSessionDescription(sdp_str);
		} else {
			// ＳＤＰがない場合 ： 明らかにエラー
			throw new NullPointerException("Answer SDP is null");
		}
		
		((AbstractEntity) target).handleEvent(new EntityEvent(sdp, source));
		
	}

	private void doNotifyResponse(Response res) {
		// NOP
	}
	
	private void doSubscribeResponse(Response res) {
		// NOP
	}
	
	void authentication (ResponseEvent evt) {
		//TODO 404
		
	}
	
	/**
	 * 
	 * @param evt
	 */
	void doErrorResponse(ResponseEvent evt) {
		
	
		Response res = evt.getResponse();
		logger.warn("Error Response : " + res.getReasonPhrase());
		if (res.getStatusCode() == Response.UNAUTHORIZED) {
			// TODO 認証！
			return ;
		}
		FromHeader from = (FromHeader) res.getHeader(FromHeader.NAME);
		ToHeader to = (ToHeader) res.getHeader(ToHeader.NAME);
		SipURI target_uri = (SipURI) from.getAddress().getURI();
		SipURI source_uri = (SipURI) to.getAddress().getURI();

		Entity source = new EntityImpl(source_uri);
		Entity target = container.getEntity(target_uri.toString());
		EntityEvent e = new EntityEvent(target, source, EventType.ERROR);
		e.setStatusCode (res.getStatusCode());
		e.setMessage(res.getReasonPhrase());
		// エラーのときはトランザクションは作らない
		//core.sendAck(source);
		if (target instanceof AbstractEntity)
			((AbstractEntity) target).handleEvent(e);
		else
			target.receiveErrorResponse(e);
		
		if (res.getStatusCode() == Response.SERVICE_UNAVAILABLE) {
			// BYEを一応送る
			try {
				core.sendBye(target);
			} catch (SipException e1) {
				e1.printStackTrace();
			}
		}
	}

	private StatusCode getStatusCode(int code) {
		String c = code + "";
		if (c.indexOf("1") == 0) {
			return StatusCode.CODE1xx;
		} else if (c.indexOf("2") == 0) {
			return StatusCode.CODE2xx;
		} else if (c.indexOf("3") == 0) {
			return StatusCode.CODE3xx;
		} else if (c.indexOf("4") == 0) {
			return StatusCode.CODE4xx;
		} else if (c.indexOf("5") == 0) {
			return StatusCode.CODE5xx;
		} else if (c.indexOf("6") == 0) {
			return StatusCode.CODE6xx;
		} else {
			throw new IllegalAccessError("Invalid Response code!");
		}
	}

	enum StatusCode {
		CODE1xx, CODE2xx, CODE3xx, CODE4xx, CODE5xx, CODE6xx;
	}

}
