package gmt.protocol.ais;

import gmt.protocol.ais.type.IECType;

import java.util.ArrayList;
import java.util.List;

public class AISDecoder {
	
	private RawAISParser parser;
	private IECType typeIEC;
	
	private List<String> rcvMessageList;	//수신받은 확장(원문)메시지 List
	private List<String> rawDataList;		//Header를 제외한 원문 메시지 List
	private List<String> collReceiveList;	//멀티메시지 처리를 위한 수신 확장(원문) 메시지 List
	private List<String> collRawDataList;	//멀티메시지 처리를 위한 Header 제외 원문 메시지 List
	
	private Double longitude;
	private Double latitude;	
	
	private Message01 msg01;
	private Message02 msg02;
	private Message03 msg03;
	private Message04 msg04;
	private Message05 msg05;
	private Message06 msg06;
	private Message07 msg07;
	private Message08 msg08;
	private Message09 msg09;
	private Message10 msg10; 
	private Message11 msg11;
	private Message12 msg12;
	private Message13 msg13;
	private Message14 msg14;
	private Message15 msg15;
	private Message16 msg16;
	private Message17 msg17;
	private Message18 msg18;
	private Message19 msg19;
	private Message20 msg20;
	private Message21 msg21;
	private Message22 msg22;
	private Message23 msg23;
	private Message24 msg24;
	private Message27 msg27;
	
	private Message06_440_40 msg06_440_40;
	private Message06_440_41 msg06_440_41;
	
	
	public AISDecoder() {
		parser = new RawAISParser();
		typeIEC = IECType.NONE;
		
		rcvMessageList 	= new ArrayList<String>(3);
		rawDataList 	= new ArrayList<String>(3);
		collReceiveList = new ArrayList<String>(3);
		collRawDataList = new ArrayList<String>(3);
		
		longitude = 181.0;
		latitude  =  91.0;
		
		msg01 = new Message01();
		msg02 = new Message02();
		msg03 = new Message03();
		msg04 = new Message04();
		msg05 = new Message05();
		msg06 = new Message06();
		msg07 = new Message07();
		msg08 = new Message08();
		msg09 = new Message09();
		msg10 = new Message10();
		msg11 = new Message11();
		msg12 = new Message12();
		msg13 = new Message13();
		msg14 = new Message14();
		msg15 = new Message15();
		msg16 = new Message16();
		msg17 = new Message17();
		msg18 = new Message18();
		msg19 = new Message19();
		msg20 = new Message20();
		msg21 = new Message21();
		msg22 = new Message22();
		msg23 = new Message23();
		msg24 = new Message24();
		msg27 = new Message27();
		
		msg06_440_40 = new Message06_440_40();
		msg06_440_41 = new Message06_440_41();
	}
	
	private boolean isCompletMsg(String recvMsg, String data) {
		boolean result = false;
		int total;
		int seq;
		
		try {
			total = strToIntDef(data.substring(7, 8), -1);
			seq   = strToIntDef(data.substring(9, 10), -1);
			
			if (total == -1 || seq == -1) {
				collReceiveList.clear();
				collRawDataList.clear();
				return result;
			}
			
			if (total == 1 && seq == 1) {
				//단일메시지 인 경우
				collReceiveList.clear();
				collRawDataList.clear();				
				collReceiveList.add(recvMsg);
				collRawDataList.add(data);					
			} else
			if (total != 1 && total >= seq) {
				// 멀티메시지인 경우
				if (collRawDataList.size() != (seq -1)){
					collReceiveList.clear();
					collRawDataList.clear();
					if (seq == 1) {
						collReceiveList.add(recvMsg);
						collRawDataList.add(data);					
					}				
					return result;				
				} else {
					collReceiveList.add(recvMsg);
					collRawDataList.add(data);
				}					
			} else {
				// 단일메시지 또는 멀티메시지가 확인이 안되는 경우
				collReceiveList.clear();
				collRawDataList.clear();
			}
			
			if (total == seq)
				result = true;
			
			return result;
			
		} catch(Exception e) {
			return false;
		}
	}
	
	
	public int parse(String data) {
		int result = -1;
		
		this.latitude  = 181.0;
		this.longitude =  91.0;
		
		this.typeIEC = IECType.getIECType(data);
		
		if ( typeIEC != IECType.VDO && typeIEC != IECType.VDM ) {
			rcvMessageList.add(data);
			rawDataList.add(data);			
			return -2;
		}
		
		rcvMessageList.clear();
		rawDataList.clear();
		
		// 멀티메시지인지 검사
		if (isCompletMsg(data, data) == false)
			return result;
		
		for (int i = 0; i <= collRawDataList.size() -1; i++) {
			String processMsg = collRawDataList.get(i);
			
			//result 0 : 파싱완료, 1 : 멀티메시지 처리 중
			try {
				int parseResult = parser.assembleVDM(processMsg);
				
				//마지막 멀티메시지가 아닌데 파싱결과가 나온 경우 종료
				if (i != collRawDataList.size() -1 && parseResult == 0) {
					collRawDataList.clear();
					return result;
				}
			} catch (ChecksumFailedException | StartNotFoundException | VDMSentenceException e) {
				collRawDataList.clear();
				return result;
			}
		}
		
		try {
			switch(parser.msgid) {
			case(1) :	// Position  
				{	
					msg01.parse(parser.six_state);	
					this.longitude = (double) (msg01.longitude() / 600000);
					this.latitude  = (double) (msg01.latitude() / 600000);
					result = 1;
					break;
				}
			case(2) :	// scheduled Position
				{
					msg02.parse(parser.six_state);	
					this.longitude = (double) (msg02.longitude() / 600000);
					this.latitude  = (double) (msg02.latitude() / 600000);
					result = 2;
					break;					
				}
			case(3) :	// Assigned Position
				{
					msg03.parse(parser.six_state);	
					this.longitude = (double) (msg03.longitude() / 600000.0);
					this.latitude  = (double) (msg03.latitude() / 600000.0);
					result = 3;
					break;
					
				}
			case(4) :	// VDO or OtherBaseStation..
				{
					msg04.parse(parser.six_state);
					this.longitude = (double) (msg04.longitude() / 600000);
					this.latitude  = (double) (msg04.latitude() / 600000);
					result = 4;
					break;				
				}
			case(5) :	// Voyage..	
				{
					msg05.parse(parser.six_state);
					result = 5;
				}
			case(6) :	// Addressed Binary Message..
				{
					result = parseABM();
				}
			case(7) :
				{
					msg07.parse(parser.six_state);
					result = 7;
				}
			case(8) :	// Binary Broadcast Message
				{
					result = parseBBM();
				}
			case(9) :	// SAR
				{
					msg09.parse(parser.six_state);
					this.longitude = (double) (msg09.longitude() / 600000);
					this.latitude  = (double) (msg09.latitude() / 600000);
					result = 9;
				}
			case(10) :
				{
					msg10.parse(parser.six_state);
					result = 10;
				}
			case(11) :	//UTC/date response
				{
					msg11.parse(parser.six_state);
					this.longitude = (double) (msg11.longitude() / 600000);
					this.latitude  = (double) (msg11.latitude() / 600000);
					result = 11;
				}
			case(12) :	//Addressed safety related message
				{
					msg12.parse(parser.six_state);
					result = 12;
				}
			case(13) :
				{
					msg13.parse(parser.six_state);
					result = 13;
				}
			case(14) :
				{
					msg14.parse(parser.six_state);
					result = 14;
				}
			case(15) :
				{
					msg15.parse(parser.six_state);
					result = 15;
				}
			case(16) :
				{
					msg16.parse(parser.six_state);
					result = 16;
				}
			case(17) :
				{
					msg17.parse(parser.six_state);
					result = 17;
				}
			case(18) :
				{
					msg18.parse(parser.six_state);
					this.longitude = (double) (msg18.longitude() / 600000);
					this.latitude  = (double) (msg18.latitude() / 600000);
					result = 18;
				}
			case(19) :
				{
					msg19.parse(parser.six_state);
					this.longitude = (double) (msg19.longitude() / 600000);
					this.latitude  = (double) (msg19.latitude() / 600000);
					result = 19;
				}
			case(20) :
				{
					msg20.parse(parser.six_state);
					result = 20;
				}
			case(21) :
				{
					msg21.parse(parser.six_state);
					this.longitude = (double) (msg21.longitude() / 600000);
					this.latitude  = (double) (msg21.latitude() / 600000);
					result = 21;
				}
			case(22) :
				{
					msg22.parse(parser.six_state);
					result = 22;
				}
			case(23) :
				{
					msg23.parse(parser.six_state);
					result = 23;
				}
			case(24) :
				{
					msg24.parse(parser.six_state);
					result = 24;
				}
			case(25) :
				{
					result = 25;
				}
			case(26) :
				{
					result = 26;
				}
			case(27) :
				{
					msg27.parse(parser.six_state);
					result = 27;
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			return -1;
		} finally {
			if (result != -1) {
				rcvMessageList.addAll(collReceiveList);
				rawDataList.addAll(collRawDataList);
			}
			collReceiveList.clear();
			collRawDataList.clear();			
		}		
		
		return result;		
	}
	
	public int parse(String rcvMsg, String data) {
		int result = -1;
		
		this.latitude  = 181.0;
		this.longitude =  91.0;
		
		this.typeIEC = IECType.getIECType(rcvMsg);
		
		if ( typeIEC != IECType.VDO && typeIEC != IECType.VDM ) {			
			return result;
		}
		
		rcvMessageList.clear();
		rawDataList.clear();
		
		// 멀티메시지인지 검사
		if (isCompletMsg(rcvMsg, data) == false)
			return result;
		
		for (int i = 0; i <= collRawDataList.size() -1; i++) {
			String processMsg = collRawDataList.get(i);
			
			//result 0 : 파싱완료, 1 : 멀티메시지 처리 중
			try {
				int parseResult = parser.assembleVDM(processMsg);
				
				//마지막 멀티메시지가 아닌데 파싱결과가 나온 경우 종료
				if (i != collRawDataList.size() -1 && parseResult == 0) {
					collRawDataList.clear();
					return result;
				}
			} catch (ChecksumFailedException | StartNotFoundException | VDMSentenceException e) {
				collRawDataList.clear();
				return result;
			}
		}
		
		try {
			switch(parser.msgid) {
			case(1) :	// Position  
				{	
					msg01.parse(parser.six_state);	
					this.longitude = (double) (msg01.longitude() / 600000);
					this.latitude  = (double) (msg01.latitude() / 600000);
					result = 1;
					break;
				}
			case(2) :	// scheduled Position
				{
					msg02.parse(parser.six_state);	
					this.longitude = (double) (msg02.longitude() / 600000);
					this.latitude  = (double) (msg02.latitude() / 600000);
					result = 2;
					break;					
				}
			case(3) :	// Assigned Position
				{
					msg03.parse(parser.six_state);	
					this.longitude = (double) (msg03.longitude() / 600000);
					this.latitude  = (double) (msg03.latitude() / 600000);
					result = 3;
					break;
					
				}
			case(4) :	// VDO or OtherBaseStation..
				{
					msg04.parse(parser.six_state);
					this.longitude = (double) (msg04.longitude() / 600000);
					this.latitude  = (double) (msg04.latitude() / 600000);
					result = 4;
					break;				
				}
			case(5) :	// Voyage..	
				{
					msg05.parse(parser.six_state);
					result = 5;
				}
			case(6) :	// Addressed Binary Message..
				{
					result = parseABM();
				}
			case(7) :
				{
					msg07.parse(parser.six_state);
					result = 7;
				}
			case(8) :	// Binary Broadcast Message
				{
					result = parseBBM();
				}
			case(9) :	// SAR
				{
					msg09.parse(parser.six_state);
					this.longitude = (double) (msg09.longitude() / 600000);
					this.latitude  = (double) (msg09.latitude() / 600000);
					result = 9;
				}
			case(10) :
				{
					msg10.parse(parser.six_state);
					result = 10;
				}
			case(11) :	//UTC/date response
				{
					msg11.parse(parser.six_state);
					this.longitude = (double) (msg11.longitude() / 600000);
					this.latitude  = (double) (msg11.latitude() / 600000);
					result = 11;
				}
			case(12) :	//Addressed safety related message
				{
					msg12.parse(parser.six_state);
					result = 12;
				}
			case(13) :
				{
					msg13.parse(parser.six_state);
					result = 13;
				}
			case(14) :
				{
					msg14.parse(parser.six_state);
					result = 14;
				}
			case(15) :
				{
					msg15.parse(parser.six_state);
					result = 15;
				}
			case(16) :
				{
					msg16.parse(parser.six_state);
					result = 16;
				}
			case(17) :
				{
					msg17.parse(parser.six_state);
					result = 17;
				}
			case(18) :
				{
					msg18.parse(parser.six_state);
					this.longitude = (double) (msg18.longitude() / 600000);
					this.latitude  = (double) (msg18.latitude() / 600000);
					result = 18;
				}
			case(19) :
				{
					msg19.parse(parser.six_state);
					this.longitude = (double) (msg19.longitude() / 600000);
					this.latitude  = (double) (msg19.latitude() / 600000);
					result = 19;
				}
			case(20) :
				{
					msg20.parse(parser.six_state);
					result = 20;
				}
			case(21) :
				{
					msg21.parse(parser.six_state);
					this.longitude = (double) (msg21.longitude() / 600000);
					this.latitude  = (double) (msg21.latitude() / 600000);
					result = 21;
				}
			case(22) :
				{
					msg22.parse(parser.six_state);
					result = 22;
				}
			case(23) :
				{
					msg23.parse(parser.six_state);
					result = 23;
				}
			case(24) :
				{
					msg24.parse(parser.six_state);
					result = 24;
				}
			case(25) :
				{
					result = 25;
				}
			case(26) :
				{
					result = 26;
				}
			case(27) :
				{
					msg27.parse(parser.six_state);
					result = 27;
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			return -1;
		} finally {
			if (result != -1) {
				rcvMessageList.addAll(collReceiveList);
				rawDataList.addAll(collRawDataList);
			}
			collReceiveList.clear();
			collRawDataList.clear();			
		}		
		
		return result;		
	}
	
	private int parseABM() {
		int result = -1;
		
		try {
			if (parser.dac == 1) {
				switch(parser.fi) {
				case(0) :	//AIS 6 Text메시지
					{
						msg06.parse(parser.six_state);
						result = 6;
					}
				case(16) :	//Number of persons on board
					{
						
					}
				case(18) :	//Clearance time to enter port
					{
						
					}
				case(20) :	//Berthing data	
					{
						
					}
				case(23) :	//Area notice - addressed
					{
						
					}
				case(25) :	//Dangerous cargo indication
					{
						
					}
				case(28) :	//Route information - addressed
					{
						
					}
				case(32) :	//Tidal window
					{
						
					}
				default :
					{
						msg06.parse(parser.six_state);
						result = 6;								
					}
				}						
			} if (parser.dac == 440 || parser.dac == 441) {
				switch(parser.fi) {
				case(40) :
					msg06_440_40.parse(parser.six_state);
					break;
				case(41) :
					msg06_440_41.parse(parser.six_state);
					break;
				default :
					msg06.parse(parser.six_state);
				}
				result = 6;				
			} else {
				msg06.parse(parser.six_state);
				result = 6;
			}	
		} catch (Exception e) {
			e.printStackTrace();
			result = -1;
		}
		
		return result;		
	}
	
	private int parseBBM() {
		int result = -1;		
		try {
			if (parser.dac == 1) {
				switch(parser.fi) {
				case(0) :
					{
						msg08.parse(parser.six_state);
						result = 8;					
					}
				case(17) :	//VTS-generated/synthetic targets
					{
						
					}
				case(19) :	//Marine traffic signal
					{
						
					}
				case(21) :	//Weather observation report from ship
					{
						
					}
				case(22) :	//Area notice - broadcast
					{
						
					}
				case(24) :	//Extended ship static and voyage-related data
					{
						
					}
				case(26) :	//Environmental
					{
						
					}
				case(27) :	//Route information - broadcast
					{
						
					}
				case(31) :	//Meteorological and Hydrographic data
					{
						
					}
				default :
					{
						msg08.parse(parser.six_state);
						result = 8;
					}
				}
			} else {
				msg08.parse(parser.six_state);
				result = 8;
			}
			
		} catch(Exception e) {
			e.printStackTrace();
			result = -1;
		}
		
		return result;
	}	

	
	private int strToIntDef(String s, int def) {		
		try { 
			
			int intVal = Integer.parseInt(s);			
			return intVal;
			
		} catch (NumberFormatException e) {
			return def;
		}				
	}
	

	public IECType getTypeIEC() {
		return typeIEC;
	}

	public Double getLongitude() {
		return longitude;
	}

	public Double getLatitude() {
		return latitude;
	}

	public Message01 getMsg01() {
		return msg01;
	}

	public Message02 getMsg02() {
		return msg02;
	}

	public Message03 getMsg03() {
		return msg03;
	}

	public Message04 getMsg04() {
		return msg04;
	}

	public Message05 getMsg05() {
		return msg05;
	}

	public Message06 getMsg06() {
		return msg06;
	}

	public Message07 getMsg07() {
		return msg07;
	}

	public Message08 getMsg08() {
		return msg08;
	}

	public Message09 getMsg09() {
		return msg09;
	}

	public Message10 getMsg10() {
		return msg10;
	}

	public Message11 getMsg11() {
		return msg11;
	}

	public Message12 getMsg12() {
		return msg12;
	}

	public Message13 getMsg13() {
		return msg13;
	}

	public Message14 getMsg14() {
		return msg14;
	}

	public Message15 getMsg15() {
		return msg15;
	}

	public Message16 getMsg16() {
		return msg16;
	}

	public Message17 getMsg17() {
		return msg17;
	}

	public Message18 getMsg18() {
		return msg18;
	}

	public Message19 getMsg19() {
		return msg19;
	}

	public Message20 getMsg20() {
		return msg20;
	}

	public Message21 getMsg21() {
		return msg21;
	}

	public Message22 getMsg22() {
		return msg22;
	}

	public Message23 getMsg23() {
		return msg23;
	}

	public Message24 getMsg24() {
		return msg24;
	}

	public Message27 getMsg27() {
		return msg27;
	}

	public Message06_440_40 getMsg06_440_40() {
		return msg06_440_40;
	}

	public Message06_440_41 getMsg06_440_41() {
		return msg06_440_41;
	}
	
	public int getMessageID() {
		return this.parser.msgid;
	}
	
	public int getDAC() {
		return this.parser.dac;
	}
	
	public int getFI() {
		return this.parser.fi;
	}
	
	public int getASC() {
		return this.parser.asc;
	}
	
	public boolean getRcvMessageList(List<String> list){
		if (list != null) {
			list.clear();
			return list.addAll(this.rcvMessageList);
		} else
			return false;
	}
	
	public boolean getRawDataList(List<String> list){
		if (list != null) {
			list.clear();
			return list.addAll(this.rawDataList);
		} else
			return false;
	}
	

}

