package org.tyszecki.rozkladpkp;
import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Observable;
import java.util.Observer;
import org.tyszecki.rozkladpkp.ExternalDelayFetcher.ExternalDelayFetcherCallback;
import org.tyszecki.rozkladpkp.pln.PLN;
import org.tyszecki.rozkladpkp.servers.HafasServer;
import org.tyszecki.rozkladpkp.servers.ServerManager;
import android.content.Context;
import android.content.Intent;
import android.os.AsyncTask;
import android.text.format.Time;
import android.util.Log;
public class ConnectionList extends Observable{
private PLN pln;
private int seqnr = 0;
private boolean isStatic = false, notifyOnAdd = false;
private Thread thread;
private int attempts;
private int serverId = -1;
private int lastError = HafasServer.DOWNLOAD_OK;
private String SID,ZID;
private ArrayList<SerializableNameValuePair> common;
private byte[] sBuffer = new byte[512];
private final int MAX_ATTEMPTS = 15;
enum CachePolicy{NoCached, CachedIfAvailable, OnlyCached};
//CacheID: -1 = brak, 0 = podstawowy, 1 = dla widżetów
public static ConnectionList forParameters(Context c, ArrayList<SerializableNameValuePair> commonFields, CachePolicy policy, int cacheID) {
ConnectionList ret = null;
String S = null,Z = null;
for(SerializableNameValuePair p : commonFields)
{
if(p.name.equals("SID"))
S = CommonUtils.StationIDfromSID(p.value);
if(p.name.equals("ZID"))
Z = CommonUtils.StationIDfromSID(p.value);
}
if(policy == CachePolicy.OnlyCached)
{
Log.i("RozkladPKP", "Pomijam sprawdzenie, wymuszenie zwracania z cache.");
return ConnectionList.fromFile(CommonUtils.ResultsHash(S, Z, null, cacheID));
}
boolean cached = false;
if(policy != CachePolicy.NoCached)
{
Log.i("RozkladPKP", "Sprawdzam cache");
//Sprawdzmy, czy cache jest aktualny
String t = RememberedManager.cacheValidTime(c, S, Z, cacheID);
if(t != null)
{
try{
Time ct,n;
n = new Time();
n.setToNow();
ct = new Time();
ct.parse(t);
if(Time.compare(ct, n) > 0)
cached = true;
else
invalidateCache(c,S,Z, cacheID); //Jeśli cache jest nieważny, usuń go.
}catch (Exception e) {
invalidateCache(c,S,Z, cacheID);
}
}
if(!cached)
Log.i("RozkladPKP", "Cache nieobecny/nieaktualny");
}
if(!cached)
{
Log.i("RozkladPKP", "Pobieranie rozkładu z internetu");
ret = new ConnectionList();
ret.common = commonFields;
ret.SID = S;
ret.ZID = Z;
ret.fetch();
return ret;
}
else
return ConnectionList.fromFile(CommonUtils.ResultsHash(S, Z, null, cacheID));
}
private static void invalidateCache(Context c, String SID, String ZID, int cacheID) {
RememberedManager.removeCache(c, SID, ZID, cacheID);
}
private static ConnectionList fromFile(String filename){
ConnectionList ret = new ConnectionList();
FileInputStream fis;
try {
fis = RozkladPKPApplication.getAppContext().openFileInput(filename);
} catch (FileNotFoundException e) {
return ret;
}
ByteArrayOutputStream content = new ByteArrayOutputStream();
int readBytes = 0;
try {
while ((readBytes = fis.read(ret.sBuffer)) != -1)
content.write(ret.sBuffer, 0, readBytes);
} catch (IOException e) {
return ret;
}
ret.pln = new PLN(content.toByteArray());
ret.isStatic = true;
ret.contentReady();
return ret;
}
public static ConnectionList fromByteArray(ArrayList<SerializableNameValuePair> commonFields, byte[] array, int seqnr)
{
ConnectionList ret = new ConnectionList();
ret.common = commonFields;
ret.seqnr = seqnr;
ret.pln = new PLN(array);
ret.contentReady();
return ret;
}
public void fetch()
{
if(isStatic)
return;
ArrayList<SerializableNameValuePair> data = new ArrayList<SerializableNameValuePair>();
data.addAll(common);
data.add(new SerializableNameValuePair("ignoreMinuteRound", "yes"));
data.add(new SerializableNameValuePair("h2g-direct", "1"));
data.add(new SerializableNameValuePair("start", "1"));
attempts = 1;
download(data);
}
public void fetchMore(boolean next)
{
if(isStatic)
return;
seqnr++;
ArrayList<SerializableNameValuePair> data = new ArrayList<SerializableNameValuePair>();
//data.addAll(common);
data.add(new SerializableNameValuePair("REQ0HafasOptimize1", "1"));
data.add(new SerializableNameValuePair("seqnr", Integer.toString(seqnr)));
data.add(new SerializableNameValuePair("clientSystem", "android14"));
data.add(new SerializableNameValuePair("existOptimizePrice", "1"));
data.add(new SerializableNameValuePair("hcount", "0"));
data.add(new SerializableNameValuePair("ignoreMinuteRound", "yes"));
data.add(new SerializableNameValuePair("androidversion", "2.0.8"));
data.add(new SerializableNameValuePair("ident", pln.id()));
data.add(new SerializableNameValuePair("REQ0HafasScrollDir", next ? "2" : "1"));
//data.add(new SerializableNameValuePair("androidversion", "1.1.4"));
data.add(new SerializableNameValuePair("h2g-direct", "1"));
data.add(new SerializableNameValuePair("clientDevice", "google_sdk"));
data.add(new SerializableNameValuePair("clientDevice", "ANDROID"));
data.add(new SerializableNameValuePair("htype", "google_sdk"));
attempts = MAX_ATTEMPTS;
download(data);
}
private void download(final ArrayList<SerializableNameValuePair> in)
{
class DownloadTask extends AsyncTask<Void, Void, Void>{
@Override
protected Void doInBackground(Void... params) {
for(int i = 0;;++i)
{
HafasServer s = ServerManager.getServer(i);
if(s == null)
break; //Koniec serwerów
String ld = (pln != null) ? pln.ld() : null;
//!!!! ufff... dodanie tego parametru zwiększa wielokrotnie wydajność systemu.
//Bez niego program wolniej działa, a serwer Sitkola jest DDOSowany :)
//Ah ten HAFAS i jego tajemnice.
int tries = attempts;
int result;
do
{
result = s.getConnections(in, ld);
if(result == HafasServer.DOWNLOAD_OK)
{
pln = s.getPLN();
ConnectionList.this.serverId = i;
lastError = result;
return null;
}
else if(result == HafasServer.DOWNLOAD_ERROR_SERVER_FAULT)
break;
}while((result != HafasServer.DOWNLOAD_OK) && --tries > 0);
lastError = result;
}
return null;
}
@Override
protected void onPostExecute(Void result) {
contentReady();
}
}
new DownloadTask().execute();
}
private void contentReady()
{
if(pln == null || pln.conCnt == 0)
{
notifyObservers(false);
return;
}
//Dane są aktualne, nie pobieramy znów
if(ExternalDelayFetcher.isUpToDate())
{
pln.addExternalDelayInfo(ExternalDelayFetcher.getDelays());
notifyObservers(false);
}
else
{
//Powiadom dwa razy - od razu i po dostniu opóźnień.
notifyObservers(true);
ExternalDelayFetcher.requestUpdate(new ExternalDelayFetcherCallback() {
@Override
public void ready(HashMap<String, Integer> delays, boolean cached) {
pln.addExternalDelayInfo(delays);
notifyObservers(false);
}
});
}
}
@Override
public void addObserver(Observer observer) {
super.addObserver(observer);
if(notifyOnAdd)
observer.update(this, null);
}
public void notifyObservers(boolean willUpdate) {
notifyOnAdd = true;
setChanged(); //Metoda jest wywoływana tylko po modyfikacji
super.notifyObservers(new Boolean(willUpdate));
}
public void saveInCache(Context c, int cacheID)
{
if(isStatic)
return;
Intent in = new Intent(c,RememberedService.class);
if(pln != null)
in.putExtra("pln", pln.data);
if(SID == null || ZID == null)
return;
in.putExtra("SID", SID);
in.putExtra("ZID", ZID);
in.putExtra("cacheID", cacheID);
c.startService(in);
}
/**
*
* @return Numer serwera, -1 jeśli nieznany.
*/
public int getServerId()
{
return serverId;
}
public boolean scrollable()
{
return !isStatic;
}
public PLN getPLN()
{
return pln;
}
public int getSeqNr()
{
return seqnr;
}
public void abort()
{
if(thread != null && thread.isAlive())
{
thread.interrupt();
thread = null;
}
}
public int getLastError()
{
return lastError;
}
}