package fishrungames.bashgid.core;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.concurrent.Semaphore;

import android.util.Log;


import fishrungames.bashgid.MainActivity;


public class NewsManager {

	private static final String [] urlArr = {
		"https://www.bashkortostan.ru/presscenter/news/rss/",
		"http://www.bashedu.ru/rss.xml",
		"http://www.bashinform.ru/rss/all.xml",
		"http://www.minkult-rb.ru/news/rss/",
		"http://02.mvd.ru/news/rss/"
		}; 
	
	private static NewsManager instance = null;

	private static volatile boolean SynchronizationInProcess = false; //Not strict, so don't need mutex
	
	private volatile Semaphore newsRecordMutex = new Semaphore(1, true);
	private ArrayList<NewsRecord> newsRecord = new ArrayList<NewsRecord>();
	
	
	private volatile Semaphore newsSearchResultMutex = new Semaphore(1, true);
	private ArrayList<NewsRecord> newsSearchResult = new ArrayList<NewsRecord>();
	String LastSearchQuery = "";
	
	
	public static boolean CanCallSynchronization()
	{
		return !SynchronizationInProcess;
	}
	
	
	
	public static NewsManager GetInstance()
	{
		if (instance == null)
		{
			instance = new NewsManager();
		}
		
		return instance;
	}
	
	
	public static class NewsRecord
	{
		public String title;
		public String description;
		public String imageId;
		public Date pubDate;
		
		public NewsRecord(String title, String description, String imageId, Date pubDate)
		{
			this.title = title;
			this.description = description;
			this.imageId = imageId;
			this.pubDate = pubDate;
		}
		
		NewsRecord(RssReader.Rss2Item rss2Item)
		{
			this.title = rss2Item.title;
			this.description = rss2Item.description;
			
			if ((rss2Item.rss2Enclosure != null) && (DownloadManager.MimeTypeIsImage(rss2Item.rss2Enclosure.mimeType)))
			{
				this.imageId = rss2Item.rss2Enclosure.url;
			}
			else
			{
				this.imageId = "R.drawable.transparent";
			}
			
			this.pubDate = rss2Item.pubDate;
		}
	}
	

	public RemoveCallbackInterface DownloadNews(NewsUpdatedCallbackInterface callback)
	{
		DownloadNewsRunnable downloadNewsRunnable = null;
		if (CanCallSynchronization())
		{
			downloadNewsRunnable = new DownloadNewsRunnable(callback);
			
			Thread thread = new Thread(downloadNewsRunnable);
			
			thread.start();
		}
		
		return downloadNewsRunnable;
	}
	
	class DownloadNewsRunnable implements Runnable, RemoveCallbackInterface
	{
		
		private volatile Semaphore callbackMutex = new Semaphore(1, true);
		NewsUpdatedCallbackInterface callback = null;
		
		public DownloadNewsRunnable(NewsUpdatedCallbackInterface callback)
		{
			try
			{
				callbackMutex.acquire();
				try
				{		
				
					this.callback = callback;	
					
				} finally
				{
					callbackMutex.release();
				}
			} catch (InterruptedException ie)
			{
				Log.e("Error in DownloadNewsRunnable", "Error in DownloadNewsRunnable");
			}
			
		}
		
		@Override
		public void RemoveCallback()
		{
			
			try
			{
				callbackMutex.acquire();
				try
				{		
				
					callback = null;
					
				} finally
				{
					callbackMutex.release();
				}
			} catch (InterruptedException ie)
			{
				Log.e("Error in DownloadNewsRunnable", "Error in DownloadNewsRunnable");
			}
		}
		
		@Override
		public void run()
		{

	
			SynchronizationInProcess = true;	

						
			ArrayList<NewsRecord> localNewsRecord = MainActivity.getInstance().newsDataSource.getNews();
			
			ArrayList<String> imageToDownloadList = new ArrayList<String>();
			
			for (int i = 0; i < urlArr.length; i++)
			{
				
				LoadNewsAndImagesFromRss2(urlArr[i], localNewsRecord, imageToDownloadList);
				
			}
			
			Collections.sort(localNewsRecord, new NewsSortComparator());
			

			try
			{
				newsRecordMutex.acquire();
				try
				{		
					
					newsRecord = localNewsRecord;
					
					MainActivity.getInstance().newsDataSource.replaceNews(newsRecord);
					
				} finally
				{
					newsRecordMutex.release();
				}
			} catch (InterruptedException ie)
			{
				Log.e("Error in DownloadNewsRunnable", "Error in DownloadNewsRunnable");
			}
			
						
			try
			{
				callbackMutex.acquire();
				try
				{		
				
					if (callback != null)
					{
						callback.OnNewsUpdated();
					}
					
				} finally
				{
					callbackMutex.release();
				}
			} catch (InterruptedException ie)
			{
				Log.e("Error in DownloadNewsRunnable", "Error in DownloadNewsRunnable");
			}
			
			
			for (String imageToDownload : imageToDownloadList)
			{
				ImageManager.getInstance().DownloadImageIfNeeded(imageToDownload);
			}
			

			SynchronizationInProcess = false;	

		}
	}
		
	public NewsManager()
	{
		try
		{
			newsRecordMutex.acquire();
			try
			{		
				newsRecord = MainActivity.getInstance().newsDataSource.getNews();
				
			} finally
			{
				newsRecordMutex.release();
			}
		} catch (InterruptedException ie)
		{
			Log.e("Error in NewsManager constructor", "Error in NewsManager constructor");
		}
		
	}
	
	
	public ArrayList<NewsRecord> getNews()
	{
		ArrayList<NewsRecord> result = new ArrayList<NewsRecord>();

		try
		{
			newsRecordMutex.acquire();
			try
			{		
				result.addAll(newsRecord);
				
			} finally
			{
				newsRecordMutex.release();
			}
		} catch (InterruptedException ie)
		{
			Log.e("Error in NewsManager constructor", "Error in NewsManager constructor");
		}
		
		return result;
	}
	
	public ArrayList<NewsRecord> getSearchResultNews()
	{
		ArrayList<NewsRecord> result = new ArrayList<NewsRecord>();

		try
		{
			newsSearchResultMutex.acquire();
			try
			{		
				result.addAll(newsSearchResult);
				
			} finally
			{
				newsSearchResultMutex.release();
			}
		} catch (InterruptedException ie)
		{
			Log.e("Error in getSearchResultNews", "Error in getSearchResultNews");
		}
		
		return result;
	}
	
	
	public String getLastSearchQuery()
	{
		//Should always be called from main thread
		
		return LastSearchQuery;
	}
	
	public void searchNews(String searchText)
	{
		ArrayList<NewsRecord> result = new ArrayList<NewsRecord>();
		
		result = MainActivity.getInstance().newsDataSource.searchNewsByString(searchText);

		try
		{
			newsSearchResultMutex.acquire();
			try
			{		

				LastSearchQuery = searchText;
				newsSearchResult = result;
				
			} finally
			{
				newsSearchResultMutex.release();
			}
		} catch (InterruptedException ie)
		{
			Log.e("Error in getSearchResultNews", "Error in getSearchResultNews");
		}
	}
	
	
	public static ArrayList<String> ExtractEnclosureImageUrl(ArrayList<RssReader.Rss2Item> rss2ItemArr)
	{
		ArrayList<String> imageToDownloadList = new ArrayList<String>();
		
		for (RssReader.Rss2Item rss2Item : rss2ItemArr)
		{
			//Enclosure
			if (rss2Item.rss2Enclosure != null)
			{
				if (DownloadManager.MimeTypeIsImage(rss2Item.rss2Enclosure.mimeType))
				{
					imageToDownloadList.add(rss2Item.rss2Enclosure.url);
				}
			}
		}
		
		return imageToDownloadList;
	}
	
	public static ArrayList<String> ExtractDescriptionImageUrl(ArrayList<RssReader.Rss2Item> rss2ItemArr)
	{
		ArrayList<String> imageToDownloadList = new ArrayList<String>();
		
		for (RssReader.Rss2Item rss2Item : rss2ItemArr)
		{
			//And images inside text
			int i = rss2Item.description.indexOf("<img");
			
			while (i != -1)
			{
				int j = rss2Item.description.indexOf("src=\"", i);
				
				if (j != -1)
				{
					j += ("src=\"").length();
					
					int k = rss2Item.description.indexOf("\"", j);
					
					String imageUrl = rss2Item.description.substring(j, k);
		
					imageToDownloadList.add(imageUrl);
				}
				
				i = rss2Item.description.indexOf("<img", j);
			}
		}
		
		return imageToDownloadList;
	}
	
	public static ArrayList<String> ConvertRelativeToAbsoluteImageUrlList(ArrayList<String> relativeImageUrlList, String url)
	{
		ArrayList<String> absoluteImageUrlList = new ArrayList<String>();
		
		for (String relativeImageUrl : relativeImageUrlList)
		{
			if (!relativeImageUrl.startsWith("http://"))
			{
				String homeFolder = DownloadManager.GetUrlFolder(url);
				
				relativeImageUrl = homeFolder + relativeImageUrl;
			}
			
			absoluteImageUrlList.add(relativeImageUrl);
		}
		
		return absoluteImageUrlList;
	}
	
	
	static void ReplaceRemoteUrlToLocalUrl(ArrayList<RssReader.Rss2Item> rss2ItemArr, ArrayList<String> relativeImageUrlList, ArrayList<String> absoluteImageUrlList)
	{

		for (RssReader.Rss2Item rss2Item : rss2ItemArr)
		{
			for (int i = 0; i < relativeImageUrlList.size(); i++)
			{
				String fileName = ImageManager.getInstance().GetImageLocalUrl(absoluteImageUrlList.get(i));
			
				rss2Item.description = rss2Item.description.replace("\"" +relativeImageUrlList.get(i) + "\"" , "\"" +fileName + "\"");
			}
		}
	}
	
	static void LoadNewsAndImagesFromRss2(String url, ArrayList<NewsRecord> newsRecordArr, ArrayList<String> imageUrlArr)
	{
		//Xperimental -- need to optimize this code. slow block BEGINS
		String xmlCode = DownloadManager.getXmlFromUrl(url);
		
		if (xmlCode == null)
		{
			return;
		}
		
		//Remove BOM character if present
		if (xmlCode.startsWith("\uFEFF"))
		{
			xmlCode = xmlCode.substring(1);
	    }
		
		
		
		RssReader rssReader = new RssReader();
		
		rssReader.ReadRss(xmlCode);
		
		
		//Xperimental -- need to optimize this code. slow block ENDS
		
		imageUrlArr.addAll(ExtractEnclosureImageUrl(rssReader.rss2ItemArr));
		
		
		ArrayList<String> relativeImageUrlList = ExtractDescriptionImageUrl(rssReader.rss2ItemArr);
		
		ArrayList<String> absoluteImageUrlList = ConvertRelativeToAbsoluteImageUrlList(relativeImageUrlList, url);
		
		imageUrlArr.addAll(absoluteImageUrlList);
		
		//Xperimental -- this function is very slow, need to speed up
		ReplaceRemoteUrlToLocalUrl(rssReader.rss2ItemArr, relativeImageUrlList, absoluteImageUrlList);			
					
		for (RssReader.Rss2Item rss2Item : rssReader.rss2ItemArr)
		{
			boolean exists = false;
			
			for (NewsRecord newsRecord : newsRecordArr)
			{
				if (newsRecord.title.equals(rss2Item.title))
				{
					exists = true;
					break;
				}
			}
			
			if (!exists)
			{
				newsRecordArr.add(new NewsRecord(rss2Item));
			}
		}		
			
	}
	
	public static class NewsSortComparator implements Comparator<NewsRecord> {
	    public int compare(NewsRecord newsRecord1, NewsRecord newsRecord2) {
	        return newsRecord2.pubDate.compareTo(newsRecord1.pubDate);
	    }
	}

	
}
