package epgdump;

import java.io.IOException;
import java.io.PushbackInputStream;
import java.util.ArrayList;
import java.util.Arrays;

public class Ts {

	private static final int EOF = -1;
	
	private static final int RET_NONE = -1;
	private static final int RET_GOTO_RETRY = -2;
	private static final int RET_RETURN_NULL = -3;
	
	private static int rcount  = 0;
	private static int ridx = -1;
	
	//
	public SECcache readTS(PushbackInputStream in, ArrayList<SECcache> secs, int size) {
		
		try {
			if ( ! skip2syncbyte(in) ) {
				return null;
			}
			
	//retry:
			
			// 戻すべき残りがあるか?
			while ( true ) {
				int ret = foo1(secs);
				if ( ret >= 0 ) {
					return secs.get(ret);
				}
				else if ( ret == RET_GOTO_RETRY ) {
					continue;
				}
				
				ret = foo2(in,secs,size);
				if ( ret >= 0 ) {
					return secs.get(ret);
				}
				else if ( ret == RET_RETURN_NULL ) {
					return null;
				}
				else if ( ret == RET_GOTO_RETRY ) {
					continue;
				}
			}
		}
		catch ( Exception e ) {
			e.printStackTrace();
			return null;
		}
	}

	//
	private boolean skip2syncbyte(PushbackInputStream in) throws IOException {
		int inchar;
		
		// syncバイトまで読み飛ばし
		if ( rcount == 0 ) {
			do {
				if ( (inchar = in.read()) != EOF ) {
					if ( (inchar & 0xFF) == 0x47 ) {
						in.unread(inchar);
						break;
					}
				}
			} while ( inchar != EOF );
			if ( inchar == EOF ) {
				return false;
			}
		}
		return true;
	}
	
	// fooとかいうな！
	
	private int foo1(ArrayList<SECcache> secs) {
		
		if ( ridx >= 0 && secs.get(ridx).cont != 0 ) {
			// バッファチェック
			if ( (secs.get(ridx).cur.payload[secs.get(ridx).curlen] & 0xFF) == 0xFF ) {
				secs.get(ridx).cont = 0;
				secs.get(ridx).seclen = 0;
				secs.get(ridx).setlen = 0;
				secs.get(ridx).curlen = 0;
			}
			else {
				int len = secs.get(ridx).cur.payloadlen - secs.get(ridx).curlen;
				if ( len == 0 ) {
					secs.get(ridx).cont = 0;
					secs.get(ridx).seclen = 0;
					secs.get(ridx).setlen = 0;
					secs.get(ridx).curlen = 0;
				}
				else {
					/* ここでseclenが跨るようにTS分割されていると困るな @@
					   if(secs[ridx].pid == 0x12) {
					   int check = secs[ridx].cur.payload[secs[ridx].curlen] & 0xFF;
					   if(!(check == 0x4E ||
					   check == 0x4F ||
					   (check >= 0x50 && check <= 0x6F))) {
					   secs[ridx].curlen -= 3;
					   }
					   }
					*/

					int[] boff = {12};
					try { 
					secs.get(ridx).seclen = Util.getBit(secs.get(ridx).cur.payload, secs.get(ridx).curlen, boff, 12) + 3; // ヘッダ
					}
					catch ( Exception e ) {
						System.err.println(secs.get(ridx).curlen);
						e.printStackTrace();
					}
					
					/*
					  if(secs[ridx].seclen == 2334) {
					  printf("aa");
					  }
					*/
					
					// TSデータ長-設定済みデータ長
					if ( secs.get(ridx).seclen > len ) {
						System.arraycopy(secs.get(ridx).cur.payload, secs.get(ridx).curlen, secs.get(ridx).buf, 0, len);
						//memcpy(secs[ridx].buf, &secs[ridx].cur.payload[secs[ridx].curlen], len);
						secs.get(ridx).setlen = len;
						secs.get(ridx).curlen = 0;
						secs.get(ridx).cont   = 1;
						// 次のレコード読み込み
					}
					else {
						System.arraycopy(secs.get(ridx).cur.payload, secs.get(ridx).curlen, secs.get(ridx).buf, 0, secs.get(ridx).seclen);
						//memcpy(secs[ridx].buf, &secs[ridx].cur.payload[secs[ridx].curlen], secs[ridx].seclen);
						secs.get(ridx).setlen = secs.get(ridx).seclen;
						secs.get(ridx).curlen += secs.get(ridx).seclen;
						secs.get(ridx).cont = 1;

						// CRCのチェック
						if ( checkcrc(secs.get(ridx)) ) {
							return ridx; /* 戻る */
						}
						return RET_GOTO_RETRY; /* もう一回 */
					}
				}
			}
		}
		return RET_NONE;
	}
	private int foo2(PushbackInputStream in, ArrayList<SECcache> secs, int size) throws IOException {
		TSpacket pk = new TSpacket();
		
		byte[] buf = new byte[1024];
		
		int[] boff = new int[1];
		int len;
		int payptr;
		
		int inchar;
		
		int roffset = 0;
		while ( true ) {
			if ( in.read(buf, roffset, 188-roffset) < 1 ) {
				/* 残りの処理? */
				return RET_RETURN_NULL;
			}
			roffset = 0;
			rcount++;

			if ( (buf[0] & 0xFF) != 0x47) {
					/* 最初はbuf中に0x47があるかチェック */
				int i = 1;
				for ( ; i<188; i++ ) {
					if( (buf[i] & 0xFF) == 0x47 ) {
						break;
					}
				}

				if ( i < 188 ) {
					/* そこから再読み込みして欲しいのでseek */
					//fseek(in, (188 - i) * -1, SEEK_CUR);
					roffset = i;
					int[] tmp = new int[188-i];
					System.arraycopy(buf, i, tmp, 0, 188-i);
					System.arraycopy(tmp, 0, buf, 0, 188-i);
					//memmove(buf, buf + i, 188 - i);
					continue;
				}

				do {
					if ( (inchar = in.read()) != EOF ) {
						if ( (inchar & 0xFF) == 0x47 ) {
							in.unread(inchar);
							break;
						}
					}
				} while ( inchar != EOF );
				if ( inchar == EOF ) {
					return RET_RETURN_NULL;
				}
				continue;
			}

			/*
			  if(rcount == 406502) {
			  printf("aa");
			  }
			*/

	    
			pk.rcount = rcount;
	    
			boff[0] = 0;
			pk.sync = Util.getBit(buf, 0, boff, 8);
			pk.transport_error_indicator = Util.getBit(buf, 0, boff, 1);
			pk.payload_unit_start_indicator = Util.getBit(buf, 0, boff, 1);
			pk.transport_priority = Util.getBit(buf, 0, boff, 1);
			pk.pid = Util.getBit(buf, 0, boff, 13);
			pk.transport_scrambling_control = Util.getBit(buf, 0, boff, 2);
			pk.adaptation_field_control = Util.getBit(buf, 0, boff, 2);
			pk.continuity_counter = Util.getBit(buf, 0, boff, 4);

			/*
			  adaptation_field_control 2 bslbf
			  continuity_counter 4 uimsbf
			  if(adaptation_field_control = = '10' || adaptation_field_control = = '11'){
			  adaptation_field()
			  }
			  ...
			  adaptation_field() {
			  adaptation_field_length 8 uimsbf
			  if (adaptation_field_length > 0) {
			  discontinuity_indicator....

			  00 Reserved for future use by ISO/IEC
			  01 No adaptation_field, payload only
			  10 Adaptation_field only, no payload
			  11 Adaptation_field followed by payload

			*/

			pk.payloadlen = 184;

			if ( pk.adaptation_field_control == 2 ) {
				continue;
			}

			if ( pk.adaptation_field_control == 3 ) {
				len = Util.getBit(buf, 0, boff, 8);
				payptr = (boff[0] / 8) + len;
				pk.payloadlen -= (len + 1);
			}
			else {
				payptr = (boff[0] / 8);
			}
			if ( pk.payloadlen < 0 ) {
				continue ;
			}

			/* 
			   if the Transport Stream packet carries the first byte of a PSI section, the payload_unit_start_indicator value
			   shall be '1', indicating that the first byte of the payload of this Transport Stream packet carries the pointer_field.
			*/
			if ( pk.payload_unit_start_indicator == 1 ) {
				/* pointer_fieldはいらない */
				payptr += 1;
				pk.payloadlen -= 1;
			}
			Arrays.fill(pk.payload, (byte) 0xFF);
			System.arraycopy(buf, payptr, pk.payload, 0, pk.payloadlen);
			//memset(pk.payload, 0xFF, sizeof(pk.payload));
			//memcpy(pk.payload, payptr, pk.payloadlen);
	    
			/*
			  if(pk.rcount == 62) {
			  printf("62\n");
			  }

			  if(pk.rcount == 63) {
			  printf("63\n");
			  }
			*/
	    
			// 興味のあるpidか確認
			for ( int i=0; i<size; i++ ) {
				if ( secs.get(i).pid == pk.pid ) {
					secs.get(i).cur = pk;
					/* 途中処理中か最初か? */
					if ( secs.get(i).cont == 0 ) {
						/* 最初 セクション長を調べる */
						boff[0]= 12;
						secs.get(i).seclen = Util.getBit(secs.get(i).cur.payload, 0, boff, 12) + 3; // ヘッダ;
						/*
						  if(secs.get(i).seclen == 2334) {
						  printf("aa");
						  }
						*/

						if ( secs.get(i).seclen > secs.get(i).cur.payloadlen ) {
							System.arraycopy(secs.get(i).cur.payload, 0, secs.get(i).buf, 0, secs.get(i).cur.payloadlen);
							//memcpy(secs.get(i).buf, secs.get(i).cur.payload, secs.get(i).cur.payloadlen);
							secs.get(i).setlen = secs.get(i).cur.payloadlen;
							secs.get(i).cont = 1;
							continue;
						}
						System.arraycopy(secs.get(i).cur.payload, 0, secs.get(i).buf, 0, secs.get(i).seclen);
						//memcpy(secs.get(i).buf, secs.get(i).cur.payload, secs.get(i).seclen);
						secs.get(i).setlen = secs.get(i).seclen;
						secs.get(i).curlen = secs.get(i).seclen;
						secs.get(i).cont = 1;
						ridx = i;
						/* CRCのチェック */
						if ( checkcrc(secs.get(ridx)) ) {
							return i; /* 取り合えず戻る */
						}
						return RET_GOTO_RETRY; /* 残り処理へ */
					}
					/* セクション長-設定済み長 */
					len = secs.get(i).seclen - secs.get(i).setlen;
					if ( len > secs.get(i).cur.payloadlen ) {
						/* 全体転送 */
						System.arraycopy(secs.get(i).cur.payload, 0, secs.get(i).buf, secs.get(i).setlen, secs.get(i).cur.payloadlen);
						//memcpy(&secs.get(i).buf[secs.get(i).setlen], secs.get(i).cur.payload, secs.get(i).cur.payloadlen);
						secs.get(i).setlen += secs.get(i).cur.payloadlen;
						continue;
					}
					/* セクション長の残りを設定 */
					System.arraycopy(secs.get(i).cur.payload, 0, secs.get(i).buf, secs.get(i).setlen, len);
					//memcpy(&secs.get(i).buf[secs.get(i).setlen], secs.get(i).cur.payload, len);
					secs.get(i).setlen  = secs.get(i).seclen;
					secs.get(i).curlen += len;
					secs.get(i).cont    = 1;
					ridx = i;
					/* CRCのチェック */
					if( checkcrc(secs.get(ridx)) ) {
						return i;
					}
					return RET_GOTO_RETRY; /* 残り処理へ */
				}
			}
		}
	}
	
	// BonTest/TsStream.cppからのパクリ
	private static final int[] CrcTable = {
		0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9,	0x130476DC, 0x17C56B6B, 0x1A864DB2, 0x1E475005,	0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6, 0x2B4BCB61,	0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD,
		0x4C11DB70, 0x48D0C6C7, 0x4593E01E, 0x4152FDA9,	0x5F15ADAC, 0x5BD4B01B, 0x569796C2, 0x52568B75,	0x6A1936C8, 0x6ED82B7F, 0x639B0DA6, 0x675A1011,	0x791D4014, 0x7DDC5DA3, 0x709F7B7A, 0x745E66CD,
		0x9823B6E0, 0x9CE2AB57, 0x91A18D8E, 0x95609039,	0x8B27C03C, 0x8FE6DD8B, 0x82A5FB52, 0x8664E6E5,	0xBE2B5B58, 0xBAEA46EF, 0xB7A96036, 0xB3687D81,	0xAD2F2D84, 0xA9EE3033, 0xA4AD16EA, 0xA06C0B5D,
		0xD4326D90, 0xD0F37027, 0xDDB056FE, 0xD9714B49,	0xC7361B4C, 0xC3F706FB, 0xCEB42022, 0xCA753D95,	0xF23A8028, 0xF6FB9D9F, 0xFBB8BB46, 0xFF79A6F1,	0xE13EF6F4, 0xE5FFEB43, 0xE8BCCD9A, 0xEC7DD02D,
		0x34867077, 0x30476DC0, 0x3D044B19, 0x39C556AE,	0x278206AB, 0x23431B1C, 0x2E003DC5, 0x2AC12072,	0x128E9DCF, 0x164F8078, 0x1B0CA6A1, 0x1FCDBB16,	0x018AEB13, 0x054BF6A4, 0x0808D07D, 0x0CC9CDCA,
		0x7897AB07, 0x7C56B6B0, 0x71159069, 0x75D48DDE,	0x6B93DDDB, 0x6F52C06C, 0x6211E6B5, 0x66D0FB02,	0x5E9F46BF, 0x5A5E5B08, 0x571D7DD1, 0x53DC6066,	0x4D9B3063, 0x495A2DD4, 0x44190B0D, 0x40D816BA,
		0xACA5C697, 0xA864DB20, 0xA527FDF9, 0xA1E6E04E,	0xBFA1B04B, 0xBB60ADFC, 0xB6238B25, 0xB2E29692,	0x8AAD2B2F, 0x8E6C3698, 0x832F1041, 0x87EE0DF6,	0x99A95DF3, 0x9D684044, 0x902B669D, 0x94EA7B2A,
		0xE0B41DE7, 0xE4750050, 0xE9362689, 0xEDF73B3E,	0xF3B06B3B, 0xF771768C, 0xFA325055, 0xFEF34DE2,	0xC6BCF05F, 0xC27DEDE8, 0xCF3ECB31, 0xCBFFD686,	0xD5B88683, 0xD1799B34, 0xDC3ABDED, 0xD8FBA05A,
		0x690CE0EE, 0x6DCDFD59, 0x608EDB80, 0x644FC637,	0x7A089632, 0x7EC98B85, 0x738AAD5C, 0x774BB0EB,	0x4F040D56, 0x4BC510E1, 0x46863638, 0x42472B8F,	0x5C007B8A, 0x58C1663D, 0x558240E4, 0x51435D53,
		0x251D3B9E, 0x21DC2629, 0x2C9F00F0, 0x285E1D47,	0x36194D42, 0x32D850F5, 0x3F9B762C, 0x3B5A6B9B,	0x0315D626, 0x07D4CB91, 0x0A97ED48, 0x0E56F0FF,	0x1011A0FA, 0x14D0BD4D, 0x19939B94, 0x1D528623,
		0xF12F560E, 0xF5EE4BB9, 0xF8AD6D60, 0xFC6C70D7,	0xE22B20D2, 0xE6EA3D65, 0xEBA91BBC, 0xEF68060B,	0xD727BBB6, 0xD3E6A601, 0xDEA580D8, 0xDA649D6F,	0xC423CD6A, 0xC0E2D0DD, 0xCDA1F604, 0xC960EBB3,
		0xBD3E8D7E, 0xB9FF90C9, 0xB4BCB610, 0xB07DABA7,	0xAE3AFBA2, 0xAAFBE615, 0xA7B8C0CC, 0xA379DD7B,	0x9B3660C6, 0x9FF77D71, 0x92B45BA8, 0x9675461F,	0x8832161A, 0x8CF30BAD, 0x81B02D74, 0x857130C3,
		0x5D8A9099, 0x594B8D2E, 0x5408ABF7, 0x50C9B640,	0x4E8EE645, 0x4A4FFBF2, 0x470CDD2B, 0x43CDC09C,	0x7B827D21, 0x7F436096, 0x7200464F, 0x76C15BF8,	0x68860BFD, 0x6C47164A, 0x61043093, 0x65C52D24,
		0x119B4BE9, 0x155A565E, 0x18197087, 0x1CD86D30,	0x029F3D35, 0x065E2082, 0x0B1D065B, 0x0FDC1BEC,	0x3793A651, 0x3352BBE6, 0x3E119D3F, 0x3AD08088,	0x2497D08D, 0x2056CD3A, 0x2D15EBE3, 0x29D4F654,
		0xC5A92679, 0xC1683BCE, 0xCC2B1D17, 0xC8EA00A0,	0xD6AD50A5, 0xD26C4D12, 0xDF2F6BCB, 0xDBEE767C,	0xE3A1CBC1, 0xE760D676, 0xEA23F0AF, 0xEEE2ED18,	0xF0A5BD1D, 0xF464A0AA, 0xF9278673, 0xFDE69BC4,
		0x89B8FD09, 0x8D79E0BE, 0x803AC667, 0x84FBDBD0,	0x9ABC8BD5, 0x9E7D9662, 0x933EB0BB, 0x97FFAD0C,	0xAFB010B1, 0xAB710D06, 0xA6322BDF, 0xA2F33668,	0xBCB4666D, 0xB8757BDA, 0xB5365D03, 0xB1F740B4
	};		
	private int CalcCrc(int crc, byte[] buf, int len) {
		int c = crc;
		int n;

		for (n = 0; n < len; n++) {
			c = (c << 8) ^ CrcTable[(((c >> 24) & 0xFF) ^ buf[n]) & 0xFF];
		}

		return c;
	}
	
	//
	private boolean checkcrc(SECcache secs) {
		/* セクションの終りに置かれる4バイトのCRC32は、
		   CRC計算の結果0になるように設定される。
		   値が発生した場合は、エラーなので対象外にする */
		if ( CalcCrc(0xFFFFFFFF, secs.buf, secs.seclen) != 0 ) {
			if (Util.debug) System.err.println(String.format("tblid:0x%x CRC error", secs.buf[0]));
			return false;
		}
		return true;
	}
}
