#!/usr/bin/env python
import os, sys, glob
import subprocess
from datetime import datetime, timedelta
from gpsdate import mkdate, fmtdate, date2gpsw
from codclk import extract_clk
from gnsspod_preproc import interpolate_gnsspod 

__authors__ = ["Ming Chen"]
__license__ = "Public Domain"
__maintainer__ = "Ming Chen"
__email__ = "ming.chen@noaa.gov"
__date__  = "April 22, 2025"

'''
#####################################################################
# 
# nasa_gnss_data.py
#
# Purpose:
# This python script is used to handle GNSS product data sets from the
# NASA CDDIS data server
# https://cddis.nasa.gov/archive/gnss/products/latest/ultra/.
##
# History:  
#       Created by: Ming Chen 2026/04/22
#                   ming.chen@noaa.gov
# 
#
#
#####################################################################

'''


class NASAData:

    remote_home = 'https://cddis.nasa.gov/archive/gnss/products'
    dcbbias='https://cddis.nasa.gov/archive/gnss/products/bias/code.bia'
    products = {
               "GRG0OPSFIN":{"subdir":"final","pid":"GRG"},
               "GRG0MGXFIN":{"subdir":"final","pid":"COM"},
               "GRG0OPSRAP":{"subdir":"rapid","pid":"RAP"},
               "GRG0OPSULT":{"subdir":"ultra","pid":"ULT"},
               "COD0OPSFIN":{"subdir":"final","pid":"COD"},
               "COD0MGXFIN":{"subdir":"final","pid":"COM"},
               "COD0OPSRAP":{"subdir":"rapid","pid":"NRT"},
               "COD0OPSULT":{"subdir":"ultra","pid":"RTM"},
                }

    def __init__(self, product):
      # Check if required environment variables are set
      self.podgen_home = os.getenv('podgen_home')
      self.utils_home = os.getenv('utils_home')
      self.local_home  = os.getenv('nasa_data')
 
      if not self.podgen_home:
         print("env is not set up, source the set_env.bash file first ..")
         sys.exit(1)
      if not NASAData.valid_product(product):
         print("product {product} is not valid ..")
         sys.exit(1)

      self.product = product
      self.pid =  NASAData.products[product]["pid"]
    
 
    @staticmethod
    def valid_product(product):
        valid = False
        if product in NASAData.products: 
           valid = True
        else:
           print(f"product {product} is not valid ....")
        return valid


    def flist_cod(self, datestr, hh='00'):
        cdate = mkdate(datestr)
        yyyy = cdate.year ;  yy = cdate.year % 100
        mm = cdate.month  ; dd = cdate.day
        doy = cdate.timetuple().tm_yday  ; doy = f"{doy:03d}"

        gpswd = date2gpsw(datestr)
        gweek, gday = gpswd[:4], gpswd[4:]
           
        prod =  self.product ; plink = self.pid
        flist = {} 
        if prod == 'COD0OPSFIN'or prod =="COD0MGXFIN":
           flist = {
	            'erp': f"{prod}_{yyyy}{doy}0000_01D_01D_ERP.ERP.gz", 
	            'orb': f"{prod}_{yyyy}{doy}0000_01D_05M_ORB.SP3.gz", 
                    'clk': f"{prod}_{yyyy}{doy}0000_01D_30S_CLK.CLK.gz", 
		    'bia': f"{prod}_{yyyy}{doy}0000_01D_01D_OSB.BIA.gz"
		   }



        elif prod == 'COD0OPSRAP':
           flist = {
                    'erp': f"{prod}_{yyyy}{doy}{hh}00_01D_01D_ERP.ERP.gz", 
		    'orb': f"{prod}_{yyyy}{doy}{hh}00_01D_05M_ORB.SP3.gz",
	            'clk': f"{prod}_{yyyy}{doy}{hh}00_01D_30S_CLK.CLK.gz", 
		    'bia': f"{prod}_{yyyy}{doy}{hh}00_01D_01D_OSB.BIA.gz"
		    }
 
        elif prod == 'COD0OPSULT':
           pdate = cdate - timedelta(days=1)
           yyyyp = pdate.year ;  yyp = pdate.year % 100
           mmp = pdate.month  ; ddp = pdate.day
           doyp = pdate.timetuple().tm_yday  ; doyp = f"{doyp:03d}"
        
           flist = {
                     'erp': f"{prod}_{yyyyp}{doyp}{hh}00_02D_01D_ERP.ERP.gz", 
		     'orb': f"{prod}_{yyyyp}{doyp}{hh}00_02D_05M_ORB.SP3.gz", 
		     'clk': f"{prod}_{yyyyp}{doyp}{hh}00_02D_30S_CLK.CLK.gz", 
		     'bia': f"{prod}_{yyyy}{doy}{hh}00_DCB.BIA.gz"
		   }
        else:

           print(f"{product} is not in the list ...")
        return flist


    def flist_grg(self, datestr, hh='00'):
        cdate = mkdate(datestr)
        yyyy = cdate.year ;  yy = cdate.year % 100
        mm = cdate.month  ; dd = cdate.day
        doy = cdate.timetuple().tm_yday
        doy = f"{doy:03d}"

        gpswd = date2gpsw(datestr)
        gweek, gday = gpswd[:4], gpswd[4:]

        flist = {} ;  flink = {}
           
        prod =  self.product ; plink = self.pid
        if prod == 'GRG0OPSFIN'or prod =="GRG0MGXFIN":
           flist = {
                   'erp': f"{prod}_{yyyy}{doy}0000_01D_01D_ERP.ERP.gz", 
		   'orb': f"{prod}_{yyyy}{doy}0000_01D_05M_ORB.SP3.gz", 
                   'clk': f"{prod}_{yyyy}{doy}0000_01D_30S_CLK.CLK.gz", 
		   'bia': f"{prod}_{yyyy}{doy}0000_01D_01D_OSB.BIA.gz"
		   }


        elif prod == 'GRG0OPSRAP':
           flist = {
                   'erp': f"{prod}_{yyyy}{doy}{hh}00_01D_01D_ERP.ERP.gz", 
		   'orb': f"{prod}_{yyyy}{doy}{hh}00_01D_05M_ORB.SP3.gz",
	           'clk': f"{prod}_{yyyy}{doy}{hh}00_01D_30S_CLK.CLK.gz", 
		   'bia': f"{prod}_{yyyy}{doy}{hh}00_01D_01D_OSB.BIA.gz"
		  }
 
        elif prod == 'GRG0OPSULT':
           pdate = cdate - timedelta(days=1)
           yyyyp = pdate.year ;  yyp = pdate.year % 100
           mmp = pdate.month  ; ddp = pdate.day
           doyp = pdate.timetuple().tm_yday  ; doyp = f"{doyp:03d}"
           flist = {
                   'erp': f"{prod}_{yyyyp}{doyp}{hh}00_02D_01D_ERP.ERP.gz",
		   'orb': f"{prod}_{yyyyp}{doyp}{hh}00_2D_05M_ORB.SP3.gz",
	           'clk': f"{prod}_{yyyyp}{doyp}{hh}00_02D_05M_CLK.CLK.gz",
		   'bia': f"{prod}_{yyyy}{doy}{hh}00_DCB.BIA.gz"
		   }
        else:

           print(f"{product} is not in the list ...")
        return flist


    def file_path(self, datestr):
        # Construct paths
        product = self.product
        cdate = mkdate(datestr)
        delta = datetime.today() - cdate
        yyyy = cdate.year
        doy = cdate.timetuple().tm_yday
        doy = f"{doy:03d}"
        gpswd = date2gpsw(datestr)
        gweek, gday = gpswd[:4], gpswd[4:]
        subdir = NASAData.products[product]['subdir']
        remote_path = f"{NASAData.remote_home}//latest/{subdir}"
        if delta.days  >= 12:
            remote_path = f"{NASAData.remote_home}/{gweek}"
        local_path = f"{self.local_home}/{product}/{yyyy}/{doy}"
        print(yyyy, doy, subdir,self.local_home)
        return (local_path, remote_path)
	
    def file_list(self, datestr, hh='00'):
        product = self.product
        if product[:3] == 'COD':
           flist = self.flist_cod(datestr, hh=hh)
        elif product[:3] == 'GRG':
           flist = self.flist_grg(datestr, hh=hh)
        return flist
	
    def download(self, datestr, hh='00'):
        # Download files	 
        product = self.product
        local_path, remote_path = self.file_path(datestr)
        flist = self.file_list(datestr, hh=hh)
        print(f"Remote URL: {remote_path}")
        print(f"Local Data Archive Path: {local_path}")
        ftypes = ['erp','orb','clk','bia']
        if product == 'GRG0OPSULT': ftypes = ['erp','orb','clk' ]
        if product == 'COD0OPSULT': ftypes = ['erp','orb' ]
        for k in ftypes:
           fn  = flist[k]
           local_file = os.path.join(local_path, fn)
           if os.path.exists(local_file):
              print(f"{local_file} already exists ...")
              continue

           if not os.path.exists(local_path):
              os.makedirs(local_path)

           #cmd="wget -q ${remote_path}/$fn -O ${local_path}/${fn}"
           #cmd='wget --load-cookies ~/.urs_cookies --save-cookies ~/.urs_cookies --keep-session-cookies  -O '
           #cmd='curl -b ~/.urs_cookies -c ~/.urs_cookies -L -n -f -o '
           cmd=f"wget --auth-no-challenge {remote_path}/{fn} -O {local_path}/{fn}"
           print(cmd)
           subprocess.run(cmd, shell=True)

           # Check if the file was downloaded successfully
           if not os.path.exists(local_file):
              print(f"File download failed ...  {local_file}")

           if os.path.exists(local_file) and os.path.getsize(local_file) == 0:
              print(f"download failed ... {remote_path}/{fn}")
              os.remove(local_file)

    def codclk_ultra(self, datestr, hh='00'):
        # Download files	 
        prod = self.product
        cdate = mkdate(datestr)
        yyyy = cdate.year 
        mm = cdate.month  ; dd = cdate.day

        local_path, remote_path = self.file_path(datestr)
        print(f"Local Data Archive Path: {local_path}")
        flist = self.file_list(datestr, hh=hh)

        fnclk = flist['clk'][:-3] ;  fnsp3 = flist['orb'][:-3]

        local_sp3 = os.path.join(local_path, fnsp3)
        local_clk = os.path.join(local_path, fnclk)
        if os.path.exists(f'{local_clk}.gz'):
           print(f"{local_clk}.gz already exists ...")
           return

        if not os.path.exists(f'{local_sp3}.gz'):
           print(f"{local_sp3}.gz not exists for deriving clk...")
           return
         
        subprocess.run(['gunzip', f'{local_sp3}.gz'])
        interpolate_gnsspod(local_sp3, local_clk, resolution=30)
        extract_clk(local_clk, local_clk, f'{yyyy}{mm}{dd}T00:00:00')
        subprocess.run(['gzip', f'{local_sp3}'])
        if os.path.exists(local_clk) and os.path.getsize(local_clk) == 0:
           print(f"deriving clk failed ... {local_clk}")
           os.remove(local_clk)
        else:
           subprocess.run(['gzip', local_clk])
	   
    def coddcb_ultra(self, datestr, hh='00'):
        # Download files	 
        prod = self.product
        cdate = mkdate(datestr)
        local_path, remote_path = self.file_path(datestr)
        flist = self.file_list(datestr, hh=hh)
        fn  = flist['bia']  
        print(f"Remote URL: {self.dcbbias}")
        print(f"Local Data Archive Path: {local_path}")
        bias_file = os.path.join(local_path, fn)
        local_file  = bias_file[:-3]
        if os.path.exists(bias_file):
           print(f"{bias_file} already exists ...")
           return

        if not os.path.exists(local_path):
           os.makedirs(local_path)

        cmd=f"wget --auth-no-challenge {self.dcbbias} -O {local_file}"
        print(cmd)
        subprocess.run(cmd, shell=True)

        # Check if the file was downloaded successfully
        if not os.path.exists(local_file):
           print(f"File download failed ...  {local_file}")

        if os.path.exists(local_file):
           if os.path.getsize(local_file) == 0:
              print(f"download failed ... {self.dcbbias}")
              os.remove(local_file)
           else:
              subprocess.run(['gzip', local_file])

def local_nasa_gnss(product:str, pdate:str, hh='00'):

   if not NASAData.valid_product(product):
      sys.exit(1)
   nasa = NASAData(product)

   local_path,_ = nasa.file_path(pdate)
   flist = nasa.file_list(pdate,hh=hh)

   flist = {k: f'{local_path}/{v}' for k, v in flist.items() } 
   return flist

def wget_nasa_gnss(product:str, pdate:str, hour=0):

   if not NASAData.valid_product(product):
      sys.exit(1)
    
   pdate = mkdate(pdate)
   hh=f'{hour:02d}'
 
   nasa = NASAData(product)

   print(f"remote nasa url: {nasa.remote_home}")
   print(f"local nasa data home: {nasa.local_home}")
   print(f"product-date: {pdate.strftime('%Y-%m-%d')} hour: {hh}")


   # Loop over dates
   datestr = pdate.strftime("%Y%m%d")
   gpswd = date2gpsw(datestr)
   gweek = gpswd[:4]
   gday = gpswd[4:]
   print(f"cdate, GWEEK, GDAY: {pdate.strftime('%Y%m%d')} {gweek} {gday} {gpswd} ")
   nasa.download(datestr,hh=hh)
   if product == 'COD0OPSULT':
      nasa.codclk_ultra(datestr,hh=hh)
      nasa.coddcb_ultra(datestr,hh=hh)
      

if __name__ == "__main__":
   import argparse

   parser = argparse.ArgumentParser(description='wget nasa data')
   parser.add_argument("-v","--verbose", action='store_true',  help="print info for debug")
   parser.add_argument("product", type=str,  help="CODE GNSS products: COD0OPSFIN,COD0MGXFIN,COD0OPSRAP,COD0OPSULT ")
   parser.add_argument("prod_date", type=str,  help="date in yyyymmdd yyyydoy ")
   parser.add_argument("--hour", type=int,  default=0, help="hour of the date")
   args = parser.parse_args()
   product = args.product
   pdate = args.prod_date  
   hh=args.hour
   if not NASAData.valid_product(product):
      sys.exit(1)
   wget_nasa_gnss(product, pdate, hour=hh)
 
 
