/************************************************************************\
 *                                                                       *
 * Libre Droid - A GNU FM streaming music client for the Android mobile  *
 * platform                                                              *
 * Copyright (C) 2009 Free Software Foundation, Inc                      *
 *                                                                       *
 * This program is free software: you can redistribute it and/or modify  *
 * it under the terms of the GNU General Public License as published by  *
 * the Free Software Foundation, either version 3 of the License, or     *
 * (at your option) any later version.                                   *
 *                                                                       *
 * This program 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 General Public License for more details.                          *
 *                                                                       *
 * You should have received a copy of the GNU General Public License     *
 * along with this program.  If not, see <http://www.gnu.org/licenses/>. *
 *                                                                       *
 *************************************************************************/

package fm.libre.droid;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.params.CoreProtocolPNames;

import android.app.ListActivity;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.MediaScannerConnection;
import android.media.MediaScannerConnection.MediaScannerConnectionClient;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.os.IBinder;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ViewAnimator;

public class LibreDroid extends ListActivity {
	private LibreServiceConnection libreServiceConn;
	private String username;
	private String password;
	private ArrayList<String> stations;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		libreServiceConn = new LibreServiceConnection();
		bindService(new Intent(this, LibreService.class), libreServiceConn,	Context.BIND_AUTO_CREATE);

		this.registerReceiver(new MediaButtonReceiver(), new IntentFilter(Intent.ACTION_MEDIA_BUTTON));
		this.registerReceiver(new UIUpdateReceiver(), new IntentFilter("LibreDroidNewSong"));
		setContentView(R.layout.main);
		
		// Load settings
		final SharedPreferences settings = getSharedPreferences("LibreDroid", MODE_PRIVATE);
		username = settings.getString("Username", "");
		password = settings.getString("Password", "");

		final EditText usernameEntry = (EditText) findViewById(R.id.usernameEntry);
		final EditText passwordEntry = (EditText) findViewById(R.id.passwordEntry);
		usernameEntry.setText(username);
		passwordEntry.setText(password);

		final Button loginButton = (Button) findViewById(R.id.loginButton);
		loginButton.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				Editor editor = settings.edit();
				editor.putString("Username", usernameEntry.getText().toString());
				editor.putString("Password", passwordEntry.getText().toString());
				editor.commit();

				LibreDroid.this.login();
			}
		});

		stations = new ArrayList<String>();
		try {
			BufferedReader stationReader = new BufferedReader(new InputStreamReader(openFileInput("libredroid-custom-stations.conf")));
			String station;
			while((station = stationReader.readLine()) != null) {
				stations.add(station);
			}
			stationReader.close();
		} catch (IOException ex) {
			Log.d("libredroid", ex.getMessage());
		}
		// Add default stations if empty
		if(stations.isEmpty()) {
			String radioButtons[] = { "Folk", "Rock", "Metal", "Classical", "Pop",
					"Punk", "Jazz", "Blues", "Rap", "Ambient", "Add A Custom Station..." };
			stations.addAll(Arrays.asList(radioButtons));
		}
		setListAdapter(new ArrayAdapter<String>(this, R.layout.list_item, stations));
		
		Button tagStation = (Button) findViewById(R.id.tagStationButton);
		tagStation.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				LibreDroid.this.nextPage();
			}
		});
		
		Button loveStation = (Button) findViewById(R.id.loveStationButton);
		loveStation.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				LibreDroid.this.libreServiceConn.service.tuneStation("user", username + "/loved");
				LibreDroid.this.nextPage();
				LibreDroid.this.nextPage();
				LibreDroid.this.nextPage();
			}
		});
		
		Button communityLoveStation = (Button) findViewById(R.id.communityStationButton);
		communityLoveStation.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				LibreDroid.this.libreServiceConn.service.tuneStation("community", "loved");
				LibreDroid.this.nextPage();
				LibreDroid.this.nextPage();
				LibreDroid.this.nextPage();
			}
		});

		final ImageButton nextButton = (ImageButton) findViewById(R.id.nextButton);
		nextButton.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				LibreDroid.this.libreServiceConn.service.next();
			}
		});
		final ImageButton prevButton = (ImageButton) findViewById(R.id.prevButton);
		prevButton.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				LibreDroid.this.libreServiceConn.service.prev();
			}
		});
		final ImageButton playPauseButton = (ImageButton) findViewById(R.id.playPauseButton);
		playPauseButton.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				LibreDroid.this.togglePause();
			}
		});
		final ImageButton saveButton = (ImageButton) findViewById(R.id.saveButton);
		saveButton.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				LibreDroid.this.save();
			}
		});
		final ImageButton loveButton = (ImageButton) findViewById(R.id.loveButton);
		loveButton.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				LibreDroid.this.libreServiceConn.service.love();
			}
		});
		final ImageButton banButton = (ImageButton) findViewById(R.id.banButton);
		banButton.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				LibreDroid.this.libreServiceConn.service.ban();
			}
		});
		final Button addStationButton = (Button) findViewById(R.id.addStationButton);
		addStationButton.setOnClickListener(new OnClickListener() {
			public void onClick(View v) {
				LibreDroid.this.addStation();
			}
		});
	}

	@Override
	public void onDestroy() {
		super.onDestroy();
	}
	
	@Override
	public void onListItemClick(ListView l, View v, int pos, long id) {
		super.onListItemClick(l, v, pos, id);
		String selection = l.getItemAtPosition(pos).toString();
		if(selection.equals(R.string.add_station)) {			
			LibreDroid.this.nextPage();
		} else {
			LibreDroid.this.libreServiceConn.service.tuneStation("globaltags", selection);
			LibreDroid.this.nextPage();
			LibreDroid.this.nextPage();
		}
	}

	public void updateSong() {
		Song song = libreServiceConn.service.getSong();
		final TextView titleText = (TextView) findViewById(R.id.titleText);
		final TextView artistText = (TextView) findViewById(R.id.artistText);
		final TextView stationText = (TextView) findViewById(R.id.stationNameText);
		final ImageView albumImage = (ImageView) findViewById(R.id.albumImage);
		final ImageButton playPauseButton = (ImageButton) findViewById(R.id.playPauseButton);
		playPauseButton.setImageResource(R.drawable.pause);
		titleText.setText(song.title);
		artistText.setText(song.artist);
		stationText.setText(libreServiceConn.service.getStationName());
		if (song.imageURL.length() > 0) {
			new AlbumImageTask().execute(song.imageURL);
		} else {
			albumImage.setImageResource(R.drawable.album);
		}
	}
	
	public void addStation() {
		final EditText stationEntry = (EditText) findViewById(R.id.stationEntry);
		stations.add(0, stationEntry.getText().toString());
		try{
			FileOutputStream stationFile = openFileOutput("libredroid-custom-stations.conf", Context.MODE_PRIVATE);
			for (String station : stations) {
				stationFile.write(station.getBytes());
				stationFile.write('\n');
			}
			stationFile.close();
		} catch (IOException ex) {
			Log.w("libredroid", ex.getMessage());
		}
		setListAdapter(new ArrayAdapter<String>(this, R.layout.list_item, stations));
		this.prevPage();
	}

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		if ((keyCode == KeyEvent.KEYCODE_BACK)) {
			if (this.libreServiceConn.service.getCurrentPage() > 0) {
				if(this.libreServiceConn.service.getCurrentPage() == 4) {
					// Back three pages if we're on the player page to get back to the main menu
					this.prevPage();
					this.prevPage();
				}
				this.prevPage();
				LibreDroid.this.libreServiceConn.service.stop();
				return true;
			}
		}
		return super.onKeyDown(keyCode, event);
	}

	public String httpGet(String url) throws URISyntaxException,
			ClientProtocolException, IOException {
		DefaultHttpClient client = new DefaultHttpClient();
		URI uri = new URI(url);
		HttpGet method = new HttpGet(uri);
		HttpResponse res = client.execute(method);
		ByteArrayOutputStream outstream = new ByteArrayOutputStream();
		res.getEntity().writeTo(outstream);
		return outstream.toString();
	}

	public String httpPost(String url, String... params)
			throws URISyntaxException, ClientProtocolException, IOException {
		DefaultHttpClient client = new DefaultHttpClient();
		URI uri = new URI(url);
		HttpPost method = new HttpPost(uri);
		List<NameValuePair> paramPairs = new ArrayList<NameValuePair>(2);
		for (int i = 0; i < params.length; i += 2) {
			paramPairs.add(new BasicNameValuePair(params[i], params[i + 1]));
		}
		method.setEntity(new UrlEncodedFormEntity(paramPairs));
		method.getParams().setBooleanParameter(
				CoreProtocolPNames.USE_EXPECT_CONTINUE, false); // Disable
																// expect-continue,
																// caching
																// server
																// doesn't like
																// this
		HttpResponse res = client.execute(method);
		ByteArrayOutputStream outstream = new ByteArrayOutputStream();
		res.getEntity().writeTo(outstream);
		return outstream.toString();
	}

	public void togglePause() {
		final ImageButton playPauseButton = (ImageButton) findViewById(R.id.playPauseButton);
		if (libreServiceConn.service.isPlaying()) {
			playPauseButton.setImageResource(R.drawable.play);
		} else {
			playPauseButton.setImageResource(R.drawable.pause);
		}
		libreServiceConn.service.togglePause();
	}

	public void login() {
		final EditText usernameEntry = (EditText) findViewById(R.id.usernameEntry);
		final EditText passwordEntry = (EditText) findViewById(R.id.passwordEntry);
		username = usernameEntry.getText().toString();
		password = passwordEntry.getText().toString();
		boolean loggedIn = libreServiceConn.service.login(username, password);
		if (loggedIn) {
			nextPage();
		}
	}

	public void nextPage() {
		final ViewAnimator view = (ViewAnimator) findViewById(R.id.viewAnimator);
		view.showNext();
		libreServiceConn.service.setCurrentPage(view.getDisplayedChild());
	}

	public void prevPage() {
		final ViewAnimator view = (ViewAnimator) findViewById(R.id.viewAnimator);
		view.showPrevious();
		libreServiceConn.service.setCurrentPage(view.getDisplayedChild());
	}

	public void save() {
		Song song = this.libreServiceConn.service.getSong();
		Toast.makeText(LibreDroid.this, getString(R.string.downloading, song.title),
				Toast.LENGTH_LONG).show();
		new DownloadTrackTask().execute(song);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		MenuItem changeStation = menu.add(0, Menu.FIRST, 0, "Change Station")
				.setIcon(R.drawable.back);
		MenuItem quit = menu.add(0, 2, 0, R.string.quit).setIcon(R.drawable.quit);
		changeStation.setOnMenuItemClickListener(new OnMenuItemClickListener() {
			public boolean onMenuItemClick(MenuItem item) {
				final ViewAnimator view = (ViewAnimator) findViewById(R.id.viewAnimator);
				if (view.getDisplayedChild() == 4) {
					LibreDroid.this.libreServiceConn.service.stop();
					LibreDroid.this.prevPage();
					LibreDroid.this.prevPage();
					LibreDroid.this.prevPage();
					return true;
				} else {
					return false;
				}
			}
		});

		quit.setOnMenuItemClickListener(new OnMenuItemClickListener() {
			public boolean onMenuItemClick(MenuItem item) {
				LibreDroid.this.libreServiceConn.service.stop();
				LibreDroid.this.finish();
				return true;
			}
		});

		return super.onCreateOptionsMenu(menu);
	}

	private class AlbumImageTask extends AsyncTask<String, String, Bitmap> {

		protected Bitmap doInBackground(String... params) {
			String url = params[0];
			Bitmap bm = null;
			try {
				URL aURL = new URL(url);
				URLConnection conn = aURL.openConnection();
				conn.connect();
				InputStream is = conn.getInputStream();
				BufferedInputStream bis = new BufferedInputStream(is);
				bm = BitmapFactory.decodeStream(bis);
				bis.close();
				is.close();
			} catch (IOException e) {

			}
			return bm;
		}

		protected void onPostExecute(Bitmap bm) {
			final ImageView albumImage = (ImageView) findViewById(R.id.albumImage);
			albumImage.setImageBitmap(bm);
		}

	}

	private class DownloadTrackTask extends
			AsyncTask<Song, String, List<Object>> implements
			MediaScannerConnectionClient {

		private MediaScannerConnection msc;
		private String path;

		@Override
		protected List<Object> doInBackground(Song... params) {
			Song song = params[0];
			List<Object> res = new ArrayList<Object>();
			try {
				File root = Environment.getExternalStorageDirectory();
				if (!Environment.getExternalStorageState().equals(
						Environment.MEDIA_MOUNTED)) {
					res.add(false);
					res.add(R.string.sd_card_needed + " " + Environment.getExternalStorageState());
					return res;
				}
				File musicDir = new File(root, "Music");
				if (!musicDir.exists()) {
					musicDir.mkdir();
				}

				File f = new File(musicDir, song.artist + " - " + song.title + ".ogg");
				this.path = f.getAbsolutePath();
				FileOutputStream fo = new FileOutputStream(f);
				URL aURL = new URL(song.location);
				HttpURLConnection conn = (HttpURLConnection) aURL.openConnection();
				conn.connect();
				if (conn.getResponseCode() == 301 || conn.getResponseCode() == 302 || conn.getResponseCode() == 307) {
					// Redirected
					aURL = new URL(conn.getHeaderField("Location"));
					conn = (HttpURLConnection) aURL.openConnection();
				}
				InputStream is = conn.getInputStream();
				BufferedInputStream bis = new BufferedInputStream(is);
				BufferedOutputStream bos = new BufferedOutputStream(fo);
				byte buf[] = new byte[1024];
				int count = 0;
				while ((count = bis.read(buf, 0, 1024)) != -1) {
					bos.write(buf, 0, count);
				}
				bos.close();
				fo.close();
				bis.close();
				is.close();
				res.add(true);
				res.add(getString(R.string.finished_download, song.title));
			} catch (Exception ex) {
				res.add(false);
				res.add(getString(R.string.download_failed, song.title)
						+ ex.getMessage());
			}
			return res;
		}

		protected void onPostExecute(List<Object> result) {
			Boolean res = (Boolean) result.get(0);
			String msg = (String) result.get(1);
			if (res.booleanValue() == true) {
				// Update the media library so it knows about the new file
				this.msc = new MediaScannerConnection(LibreDroid.this, this);
				this.msc.connect();
			}
			Toast.makeText(LibreDroid.this, msg, Toast.LENGTH_LONG).show();
		}

		public void onMediaScannerConnected() {
			this.msc.scanFile(this.path, null);
		}

		public void onScanCompleted(String path, Uri uri) {

		}

	}

	private class UIUpdateReceiver extends BroadcastReceiver {

		@Override
		public void onReceive(Context context, Intent intent) {
			LibreDroid.this.runOnUiThread(new Runnable() {
				public void run() {
					LibreDroid.this.updateSong();
				}
			});
		}

	}

	private class MediaButtonReceiver extends BroadcastReceiver {

		@Override
		public void onReceive(Context context, Intent intent) {
			KeyEvent ev = (KeyEvent) intent.getExtras().get(
					Intent.EXTRA_KEY_EVENT);
			if (ev.getAction() == KeyEvent.ACTION_UP) {
				// Only perform the action on keydown/multiple
				return;
			}
			switch (ev.getKeyCode()) {
			case KeyEvent.KEYCODE_MEDIA_NEXT:
				LibreDroid.this.libreServiceConn.service.next();
				this.abortBroadcast();
				break;
			case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
				LibreDroid.this.libreServiceConn.service.prev();
				this.abortBroadcast();
				break;
			case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
				LibreDroid.this.togglePause();
				this.abortBroadcast();
				break;
			}
		}

	}

	private class LibreServiceConnection implements ServiceConnection {

		public ILibreService service = null;

		public void onServiceConnected(ComponentName name, IBinder service) {
			this.service = (ILibreService) service;
			LibreDroid.this.runOnUiThread(new Runnable() {
				public void run() {
					final ViewAnimator view = (ViewAnimator) LibreDroid.this.findViewById(R.id.viewAnimator);
					view.setDisplayedChild(LibreServiceConnection.this.service.getCurrentPage());
					if (LibreServiceConnection.this.service.getCurrentPage() == 2) {
						LibreDroid.this.updateSong();
					}
				}
			});

		}

		public void onServiceDisconnected(ComponentName name) {

		}

	}

}