package org.ccalm.weather;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.TimeZone;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;

import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.core.io.ClassPathResource;
import org.springframework.http.CacheControl;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.CrossOrigin;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.ServletContextAware;
import org.w3c.dom.Document;

import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

//import main.DownloadFromHTTP;
import org.ccalm.weather.WeatherDownload;
import ucar.ma2.Array;
import ucar.nc2.Dimension;
import ucar.nc2.Variable;
import ucar.nc2.dataset.NetcdfDataset;

@Controller
public class AirTemperature implements ServletContextAware {
	
	@Value("${custom.config.db_url}")
	private String db_url;
	@Value("${custom.config.db_login}")
	private String db_login;
	@Value("${custom.config.db_password}")
	private String db_password;
	@Value("${custom.config.data_dir}")
	private String data_dir;

	private static final org.slf4j.Logger logger = LoggerFactory.getLogger(SoilTmperature.class);

	private ServletContext context;

	//http://127.0.0.1:8080/AirTemperature

    @RequestMapping(value = "/geodatalist/AirTemperature",method = RequestMethod.GET,produces = "text/html;charset=UTF-8")
    @ResponseBody
    public Object ajaxTamer(/*@RequestParam(required=true,name="forecast") String forecast,*/@RequestParam(required=false,name="date") String date) {
    	String forecast = "000";

    	String result="";
    	result+="Start!<br>";

    	TimeZone.setDefault(TimeZone.getTimeZone("UTC"));

		File dir = new File(data_dir+"temp"+File.separator);
		if (!dir.exists()) dir.mkdirs();	

		//response.getWriter().append("Served at: ").append(request.getContextPath());
		Connection conn = null;
		try{
			Class.forName("org.postgresql.Driver");
			conn = DriverManager.getConnection(db_url,db_login,db_password);
			if(conn!=null)
			{
				logger.info("Connect is OK!");
				result+="Connect is OK!<br>";
			}else
			{
				logger.info("<br>Connect is ERROR<br>");
				result+="Connect is ERROR!<br>";
			}
		}catch(Exception e)
		{
			logger.info("<br>Connect Exception:"+e.getMessage()+"<br>");
			result+="Connect Exception:"+e.getMessage()+"<br>";
		}

		//Example request: http://ccalm.org/AirTemperature?date=20210531
		//Example request: http://localhost:8080/AirTemperature?date=20210531
		if(date==null || date.equals(""))
		{
			DateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
			date=dateFormat.format(new Date()); //Date like as "20170327".
		}

		String time = "00"; //00 hours,06 hours,12 hours and 18 hours
		String measurement = "TMP:2 m above ground";

		//Build URL to download
		String URL = "https://www.ftp.ncep.noaa.gov/data/nccf/com/gfs/prod/gfs."+date+"/"+time+"/atmos/gfs.t"+time+"z.pgrb2.0p25.f"+forecast;
		
	    File f = new File(data_dir+"temp"+File.separator+"air_text.idx");
	    if(f.exists()) {
		    if (!f.delete()) { 
		      System.out.println("Failed to delete the file \"air_text.idx\".");
		    }	    	
	    }
		
		WeatherDownload wd = new WeatherDownload();
		if(wd.download(URL+".idx", data_dir+"temp"+File.separator+"air_text.idx", "0", ""))
		{
			result+="Download "+URL+".idx"+" to "+data_dir+"temp"+File.separator+"air_text.idx"+"<br>";
			
			String strPos1="";
			String strPos2="";
			//Read file and find required line.
			try {
				BufferedReader br = new BufferedReader(new FileReader(data_dir+"temp"+File.separator+"air_text.idx"));
				String line;
				while ((line = br.readLine()) != null)
				{
					if (line.contains(measurement))
					{
						strPos1=line;
						strPos2=br.readLine();
						break;
					}
				}
				br.close();
			} catch (IOException ex) {
				logger.info(ex.getMessage());
				result+=ex.getMessage()+"<br>";
			}
			if(!strPos1.equals(""))
			{
				StringBuffer answer1=new StringBuffer(strPos1);
				CutBeforeFirst(answer1,":");
				String posStart = CutBeforeFirst(answer1,":");

				StringBuffer answer2=new StringBuffer(strPos2);
				CutBeforeFirst(answer2,":");
				String posEnd = CutBeforeFirst(answer2,":");
				if(posEnd==null || posEnd.equals("")) posEnd=""; else 
				{
					posEnd=String.valueOf(Long.parseLong(posEnd)-1);
				}
			
				wd.download(URL, data_dir+"temp"+File.separator+"air_text.f000", String.valueOf(posStart), String.valueOf(posEnd));
			}
		}else
		{
			result+="Not download "+URL+".idx"+" to "+data_dir+"temp"+File.separator+"air_text.idx"+"<br>";
		}

		Array dataArrayLat=null;
		Array dataArrayLon=null;
		Array dataArrayTmp=null;

		try {
            // open netcdf/grib/grib2 file from argument
            NetcdfDataset gid = NetcdfDataset.openDataset(data_dir+"temp"+File.separator+"air_text.f000");

			//logger.info("Desc: " + gid.getDescription());
			logger.info(gid.getDetailInfo());
			//logger.info("Feature type: " + gid.getFeatureType().toString());

			// get all grid tables in the file
            List<Variable> variables = gid.getReferencedFile().getVariables();
			for (int i = 0; i < variables.size(); i++)
			{
				String vName=variables.get(i).getName();
				System.out.print(vName+"  ");

				if(vName.equals("reftime"))
				{
					logger.info("");
					logger.info("Description = "+variables.get(i).getDescription());
					logger.info("DimensionsString = "+variables.get(i).getDimensionsString());
					logger.info("DataType = "+variables.get(i).getDataType());
					logger.info("UnitsString = "+variables.get(i).getUnitsString()); //Hour since 2017-02-28T18:00:00Z
				}

				if(vName.equals("lon"))
				{
					dataArrayLon = variables.get(i).read();
				}
				if(vName.equals("lat"))
				{
					dataArrayLat = variables.get(i).read();
				}
				if(vName.equals("Temperature_height_above_ground"))
				{
					dataArrayTmp = variables.get(i).read();
				}
			}

			logger.info("");
			List<Dimension> dims = gid.getDimensions();
			logger.info("dims.size() = " + dims.size());

			Iterator<Dimension> dimIt = dims.iterator();
			while( dimIt.hasNext()) {
				Dimension dim = dimIt.next();	
				logger.info("Dim = " + dim);
				logger.info("Dim name = "+dim.getName());
			}
			dimIt = null;

			Statement st=null;
			try {
				st = conn.createStatement();
			} catch (SQLException ex) {
				logger.info(ex.getMessage());
			}

			try {
				st.executeUpdate("BEGIN TRANSACTION;");
			} catch (SQLException ex) {
				logger.info(ex.getMessage());
				logger.info(ex.getMessage());
			}
			
			result+="Size="+dataArrayLat.getSize()+"<br>";

			//Delete old data
			System.out.println("Delete old data 1");
			try {
				String sql="delete from main.air_temperature where date=cast(to_timestamp('"+date+" "+time+"', 'YYYYMMDD HH24') as timestamp without time zone) and hours="+forecast;
				st.executeUpdate(sql);
			} catch (SQLException ex) {
				logger.info(ex.getMessage());
			}
			System.out.println("Delete old data 2");
			try {
				String sql="delete from main.air_temperature where date<=CURRENT_DATE-'730 days'::INTERVAL";
				st.executeUpdate(sql);
			} catch (SQLException ex) {
				logger.info(ex.getMessage());
			}
			
			
			
			int pos=0;
			for(int nLat=0;nLat<dataArrayLat.getSize();nLat++)
			{
				for(int nLon=0;nLon<dataArrayLon.getSize();nLon++)	
				{
					//WGS84 Bounds: -180.0000, -90.0000, 180.0000, 90.0000
					//Projected Bounds: -180.0000, -90.0000, 180.0000, 90.0000
					double lon = dataArrayLon.getFloat(nLon);
					if(lon>180) lon=lon-360;						
					double lat = dataArrayLat.getFloat(nLat);
					
					if(lat>29 && lat<67 && lon>17 && lon<180) //Central Asia
					{
						if(!Float.isNaN(dataArrayTmp.getFloat(pos))) //On the water none temperatyre.
						{
							String country_id="";
							ResultSet rs=null;
							try {
								String sql="select c.id from main.countries c where ST_Contains(c.geom,ST_SetSRID(st_makepoint("+lon+","+lat+"),4326)) limit 1";
								rs = st.executeQuery(sql);
							} catch (SQLException ex) {
								logger.info(ex.getMessage());
							}
							if (rs != null) {
								try {
									if (rs.next())
										country_id=rs.getString(1);
									rs.close();
								} catch (SQLException ex) {
									logger.info(ex.getMessage());
								}
							}
							if(country_id!=null && !country_id.equals("") && !country_id.equals("null"))
							{
								//logger.info(lon + "," + lat +","+dataArrayTmp.getFloat(pos));
								try {
									String sql="insert into main.air_temperature(date,hours,val,geom,country_id)values(cast(to_timestamp('"+date+" "+time+"', 'YYYYMMDD HH24') as timestamp without time zone),"+forecast+","+dataArrayTmp.getFloat(pos)+",ST_SetSRID(st_makepoint("+lon+","+lat+"),4326),"+country_id+");";
									st.executeUpdate(sql);
								} catch (SQLException ex) {
									logger.info(ex.getMessage());
								}
							}
							
						}
					}
					pos++;
				}
			}
			
			//Cut data piece from big country of Russia
			try {
				String sql="update main.air_temperature w set country_id=null where country_id=7 and not ST_Contains(ST_SetSRID(ST_GeomFromText('POLYGON((10.00 66.00,10.00 40.00,179.00 40.00,179.00 66.00,10.00 66.00))'),4326),w.geom);";
				st.executeUpdate(sql);
			} catch (SQLException ex) {
				logger.info(ex.getMessage());
			}

			//Delete values where country_id is null
			try {
				String sql="delete from main.air_temperature where country_id is null;";
				st.executeUpdate(sql);
			} catch (SQLException ex) {
				logger.info(ex.getMessage());
			}

			try {
				st.executeUpdate("END TRANSACTION;");
			} catch (SQLException ex) {
				logger.info(ex.getMessage());
				logger.info(ex.getMessage());
			}
		
            gid.close();
        } catch (IOException ex) {
            //Logger.getLogger(Main.class.getName()).log(Level.SEVERE, null, ex);
			System.out.print("ERROR!");
        }		

		try {conn.close();} catch (SQLException ex) {logger.info(ex.getMessage());}		

		result+="End!<br>";
    	return result;
    }
    //---------------------------------------------------------------------------
	@Override
	public void setServletContext(ServletContext context) {
		this.context=context;
	}
	//---------------------------------------------------------------------------
	public static String CutBeforeFirst(StringBuffer str,String ch)
	{
		int pos=str.indexOf(ch);
		String result="";
		if(pos==-1)
		{
			result.concat(str.toString());
			str.delete(0,str.length());
		}else
		{
			result=str.substring(0,pos);
			str.delete(0,pos+1);
		}
		return result;
	}
	//---------------------------------------------------------------------------
	//List of "Air temperature" dates from database in JSON
	//@CacheControl(maxAge = 300)
	@CrossOrigin
    @RequestMapping(value = "/geodatalist/AirTemperatureDates",method = RequestMethod.GET,produces = "text/html;charset=UTF-8")
    @ResponseBody
    public Object ajaxAirDates(HttpServletResponse response) {
		String headerValue = CacheControl.maxAge(60, TimeUnit.SECONDS).getHeaderValue();
		response.addHeader("Cache-Control", headerValue);

    	boolean error=false;
    	String result="";
    	
		Connection conn = null;
		try {
			Class.forName("org.postgresql.Driver");
			conn = DriverManager.getConnection(db_url, db_login, db_password);
			if (conn != null) {
				logger.info("Connect is OK!");
			} else {
		    	error=true;
		    	result="An error occurred while connecting to the database!";
			}
		} catch (Exception ex) {
			logger.info(ex.getMessage());
	    	error=true;
	    	result="<br>SQLException: "+ex.getMessage()+"<br>";
		}		
		
		if(!error)
		{
			Statement st;
			try {
				st = conn.createStatement();
				String sql = "SELECT to_char(date, 'YYYY-MM-DD') as date,hours as hour,EXTRACT(DAY FROM CURRENT_DATE-date) as day FROM main.air_temperature group by date,hours order by date,hours;";
				ResultSet rs = st.executeQuery(sql);	
				if(rs!=null)
				{
					boolean exists=false;
					result="[";
					while (rs.next())
					{
						exists=true;				
						try {
							result+= "{\"num\":\""+rs.getString("day")+"\", \"hour\":\""+rs.getString("hour")+"\", \"date\":\""+rs.getString("date")+"\"},";
						} catch( Exception ex ) 
						{
													
						}
					}				
					if(exists) {
						result=result.substring(0, result.length()-1);
						result+="]";
					}else {
						result="[]";
					}
					
				}
				st.close();
				conn.close();
			} catch (SQLException ex) {
				result="<br>SQLException:"+ex.getMessage()+"<br>";
			}	
		}		
    	return result;
    }
    //---------------------------------------------------------------------------

}
