// javascriptカート処理のコア
// 
// 本スクリプトにはUI制御は含まれず、純粋に商品の出し入れと
// クッキーへのデータ保存を実装しています。
// 実際に画面で使用するためには、別途UI制御のスクリプトが
// 必要になります。
// また、本モジュールではユーザーのログイン状態なども関知しません。
//
// 依存関係
//  jquery
//  jquery.cookie
//

/**
 * クッキーに商品情報を格納する際のセパレータ.
 */
var M2PCART_COOKIE_DELIMITER = ':::,';
var M2PCART_COOKIE_SEPARATER = '::';

/**
 * クッキーに格納する際、フルデータを保持する件数。
 * (クッキーの上限バイト数を越えないために、商品名を削除してクッキーに保存する)
 */
var M2PCART_COOKIE_DETAILMAX = 10;

/**
 * クッキー設定情報(必要に応じて利用者が上書きすることを想定).
 */
var M2PCART_COOKIE_CONFIG = {
		"domain":  '.m2plus.com',
		"path":    '/',
		"expires": '',
		"secure": false
};

/**
 * 送料として定義されている商品ikey.
 * (データベースの商品マスタに初期設定されている定数レコードに合致している必要あり)
 */
var M2PCART_TARN_IKEY = 'm2p000001';
/**
 * 「商品以外」の商品ikeyを示すプレフィクス.
 */
var M2PCART_IKEY_NOITEM = 'm2p0000';

/**
 * 注文(１商品)データをモデル化したクラス.
 * @param   id String 商品ID
 * @return
 */
function M2pCartOrder(id) {
	this.id    = id || '';	// 商品ID
	this.name  = '';			// 商品名
	this.price = 0;			// 単価(税込み)
	this.count = 1;			// 数量
	this.url   = '';			// 商品ページURL
	this.shipping = false;	// 物販の有無 ( クッキー保存時は 1:あり 0:なし )
	this.tran  = 0;			// 送料
	
	// 小計金額
	this.subtotal = function() {
		return this.price * this.count;
	};

	// 内容チェック(クッキーから読み込んだ場合の異常データ回避)
	this.validate = function() {
		if (! this.id ) return false;
		if (! this.price && this.price < 0 ) return false;
		if (! this.count && this.count <= 0 ) return false;
		
		return true;
	};
}

/**
 * カートをモデル化したクラス.
 * カート内の商品順序は、最後に入れたものが先頭になります.
 * 
 * 現在の実装では、注文データの追加・削除処理用に
 * インデックスを持たせています。
 * しかし、ユーザーがカートに追加する商品数が1～2品であれば
 * かえってパフォーマンスが悪いかもしれません。
 * 
 * 送料は、他の商品明細とは別に、M2pCartOrderインスタンスとして
 * カート内で管理しています。
 * 商品明細とは別管理なので orders() には含まれず、また
 * cookieにも保存されないのでサーバーにも送信されません。
 * 送料データは基本的にページ毎にtran()が呼ばれると構築されます。
 */
function M2pCart() {
	// 注文データを保持する( M2pCartOrderオブジェクトを格納 )
	this._orders = new Array();
	// 商品IDをキーに、注文を保持する索引.
	this._index = {};
	
	// 送料(商品明細とは分けて保持)
	this._tran = new M2pCartOrder( M2PCART_TARN_IKEY );

	// 指定した商品の注文データの取得
	// (存在しないidの場合null)
	this.get = function(id) {
		return this._index[id];
	}
	
	// 指定された商品をカートから削除.
	this.remove = function(id) {
		for (var i=0; i < this._orders.length; i++) {			
			if (id == this._orders[i].id) {
				// 配列と索引から削除
				this._orders.splice( i, 1 );
				delete this._index[id];
				
				return;
			}
		}
	};
	
	// 商品を追加する(受け取った参照を保持する)
	this.add = function(order) {
		var id = order.id;
		
		// 既に保有しているかどうかで処理を分ける.
		if ( id in this._index) {
			// 順番を先頭に持ってくるために一度削除して追加
			order.count += this._index[id].count;
			
			this.remove(id);
			this.add(order);
		} else {
			this._index[id] = order;
			this._orders.unshift( order );
		}
	};
	
	// 指定されたIDの商品数を＋１
	this.increment = function(id) {
		this._index[id].count ++;
	};
	
	// 指定されたIDの商品数をー１
	this.decrement = function(id) {
		this._index[id].count --;
		
		if ( this._index[id].count == 0 ) {
			this.remove(id);
		}
	};
		
	// カートの商品を全削除
	this.clearall = function() {
		this._orders = new Array();
		this._index = {};
	};
	
	// カート内の商品数(各注文数量の合計)
	this.count = function() {
		var result = 0;
		
		for (var id in this._index) {
			result += this._index[id].count;
		}
		
		return result;
	};
	
	// 合計金額の計算(送料は除く)
	this.total = function() {
		var result = 0;
		
		for (var id in this._index) {
			result += this._index[id].subtotal();
		}
		
		return result;
	};
	
	// 全注文データを配列で取得
	this.orders = function() {
		return this._orders;
	};
	
	// 送料データを取得する(金額ではなく、オブジェクトなので注意。物販がない場合はnullが返ります)
	this.tran = function() {
		
		var is_shipping = false;
		
		for( var i=0; i < this._orders.length; i++ ) {
			if ( ! this._orders[i].shipping ) {
				continue;
			}
			
			is_shipping = true;
			
			if ( this._tran.pric < this._orders[i].tran ) {
				this._tran.pric = this._orders[i].tran;
			}
		}
		
		return is_shipping ? this._tran : null;
	};	
}

/* -------------------------------------- */

/**
 * カートデータホルダー.
 * <br />
 * 原則として同一のインスタンスを使用し、動作中に
 * 別インスタンスがセットされることは想定していません。
 */
var m2pcart_data = new M2pCart();

/**
 * カートの最終更新タイムスタンプ.
 */
var m2pcart_timestamp = 0;


/**
 * カートの初期化メソッド.
 */
function m2pcart_init() {
	// クッキーからカート情報を復元(なければクリア)
	m2pcart_loadtimestamp();
	m2pcart_loadcookie();
}

/**
 * カートへ商品を追加する.
	
 * データホルダーに商品を追加し、クッキーへ保存
 */
function m2pcart_add(order) {
	m2pcart_data.add(order);
	// クッキーへ保存	
	m2pcart_savecookie();
}

/**
 * 現在のカート内商品数（合計数量）.
 */
function m2pcart_count() {
	return m2pcart_data.count();
}

/**
 * 現在のカート内商品数（合計数量）.
 */
function m2pcart_totalprice() {
	return m2pcart_data.total();
}

/**
 * カート内の全注文を(配列として)取得する.
 * 配列の内容は、新しいものが先頭になっています。
 * @return
 */
function m2pcart_orders() {
	return m2pcart_data.orders();
}

/**
 * カーとの商品を１減らす.
 * @param id
 * @return
 */
function m2pcart_decrease(id) {
	m2pcart_data.decrement(id);
	// クッキーへ保存	
	m2pcart_savecookie();
}

/**
 * カーとの商品を１増やす.
 * @param id
 * @return
 */
function m2pcart_increase(id) {
	m2pcart_data.increment(id);
	// クッキーへ保存	
	m2pcart_savecookie();
}

/**
 * 指定された商品をカートから削除
 * @param id
 * @return
 */
function m2pcart_remove(id) {
	m2pcart_data.remove(id);
	// クッキーへ保存	
	m2pcart_savecookie();
}

/**
 * カートの内容をすべてクリアする.
 * @return
 */
function m2pcart_clearall() {
	m2pcart_data.clearall();
	// クッキーへ保存	
	m2pcart_savecookie();	
}

/**
 * カート明細データをクッキーに保存する.
 */
function m2pcart_savecookie() {
	// 最終更新時刻の更新
	m2pcart_updatetimestamp();

	// 注文情報リストのsave
	var serialized = '';
	var orders = m2pcart_data.orders();

	// 逆順に保存しないとロード時に新しいものが下になる.
	for (var i=orders.length-1; i >= 0; i-- ) {
		// 所定番号以上のものは名前を除いて保存
		if( i > M2PCART_COOKIE_DETAILMAX ) {
			orders[i].name = '';
		}
		
		serialized += m2pcart_serialize( orders[i] );
	}
	
	$.cookie( 'm2pcart_data', serialized, M2PCART_COOKIE_CONFIG );	
}

/**
 * カート明細データをクッキーから復元する.
 */
function m2pcart_loadcookie() {
	// 現在データのクリア
	m2pcart_data.clearall();
	
	// 注文情報
	var serialized = $.cookie('m2pcart_data');

	if (! serialized) {
		return;
	}
	
	var orders = serialized.split( M2PCART_COOKIE_DELIMITER );
	
	for( var i=0; i < orders.length; i++ ) {
		var order = m2pcart_deserialize( orders[i] );
		
		if (! order) {
			continue;
		}
		
		// もしcookieに商品以外が含まれていても明細リストに含めない(送料は別途構築され、割引はサーバーで処理)
		if ( order.id.indexOf( M2PCART_IKEY_NOITEM ) == 0 ) {
			continue;
		}

		if ( order.validate() ) {
			m2pcart_data.add(order);
		}
	}
	
	// 送料を更新
	m2pcart_data.tran();
}

/**
 * 注文情報(1商品分)をクッキー出力用にシリアライズする.
 * @param order M2pCartOrder
 * @return String
 */
function m2pcart_serialize(order) {
	var serialized = '';

	serialized += order.id;
	serialized += M2PCART_COOKIE_SEPARATER;
	serialized += order.count;
	serialized += M2PCART_COOKIE_SEPARATER;
	serialized += order.price;
	serialized += M2PCART_COOKIE_SEPARATER;
	serialized += order.url;
	serialized += M2PCART_COOKIE_SEPARATER;
	serialized += order.shipping ? '1' : '0';
	serialized += M2PCART_COOKIE_SEPARATER;
	serialized += order.tran;
	serialized += M2PCART_COOKIE_SEPARATER;
	serialized += order.name;
	serialized += M2PCART_COOKIE_DELIMITER;
	
	return serialized;
}

/**
 * クッキーデータから1商品分の注文情報を復元する.
 * @param serialized String シリアル化された注文情報
 * @return M2pCartOrder
 */
function m2pcart_deserialize(serialized) {
    var attributes = serialized.split( M2PCART_COOKIE_SEPARATER );

    if ( attributes.length < 6 ) {
        return null;
    }

    var result = new M2pCartOrder();

    result.id       = attributes[0];
    result.count    = Number( attributes[1] );
    result.price    = Number( attributes[2] );
    result.url      = attributes[3];
    result.shipping = (Number(attributes[4]) == 1);
    result.tran     = Number( attributes[5] );
    result.name     = attributes[6];	// 商品名にセパレータ記号が含まれる可能性がある場合は要注意
    
    return result;
}

/**
 * カートデータのタイムスタンプを更新し、クッキーに書き込む.
 */
function m2pcart_updatetimestamp() {
	m2pcart_timestamp = new Date().getTime();
	$.cookie( 'm2pcart_timestamp', m2pcart_timestamp, M2PCART_COOKIE_CONFIG );
}

/**
 * カートデータのタイムスタンプがクッキーと一致するかどうか.
 * @return boolean 一致する場合 true
 */
function m2pcart_checktimestamp() {
	return ( m2pcart_timestamp == $.cookie('m2pcart_timestamp') );
}

/**
 * クッキーからカートタイムスタンプをロードする.
 */
function m2pcart_loadtimestamp() {
	m2pcart_timestamp = $.cookie('m2pcart_timestamp');
}

/**
 * ikeyを指定して注文を取得する.
 * @return M2pCartOrder 注文情報(存在しない場合null)
 */
function m2pcart_getorder(id) {
	return m2pcart_data.get( id );
}

/**
 * カートに含まれる商品ikeyを返す.
 * @return Array of String
 */
function m2pcart_getIkeys() {
	var orders = m2pcart_data.orders();
	var result = new Array();
	
	for (var i=0; i < orders.length; i++) {
		result.push( orders[i].id );
	}
	
	return result;
}

