import logging
import os.path as op
import numpy as num

from pyrocko import gf
from pyrocko.guts import Float

from .base import TargetGenerator, NoiseGenerator
from .station import RandomStationGenerator, StationGenerator

DEFAULT_STORE_ID = 'ak135_static'

logger = logging.getLogger('pyrocko.scenario.targets.gnss_campaign')
guts_prefix = 'pf.scenario'


class GPSNoiseGenerator(NoiseGenerator):
    measurement_duarion_days = Float.T(
        default=2.,
        help='Measurement duration in days')

    def add_noise(self, campaign):
        # https://www.nat-hazards-earth-syst-sci.net/15/875/2015/nhess-15-875-2015.pdf
        waterlevel = 1. - (.99 + .0015 * self.measurement_duarion_days)  # noqa
        logger.warning('GNSSNoiseGenerator is a work-in-progress!')

        for ista, sta in enumerate(campaign.stations):
            pass
            # rstate = self.get_rstate(ista)

            # sta.north.sigma = 8e-3
            # sta.east.sigma = 8e-3

            # sta.north.shift += rstate.normal(0., sta.north.sigma)
            # sta.east.shift += rstate.normal(0., sta.east.sigma)


class GNSSCampaignGenerator(TargetGenerator):
    station_generator = StationGenerator.T(
        default=RandomStationGenerator(
            network_name='GN',
            channels=None),
        help='The StationGenerator for creating the stations.')

    noise_generator = NoiseGenerator.T(
        default=GPSNoiseGenerator.D(),
        optional=True,
        help='Add Synthetic noise to the GNSS displacements.')

    store_id = gf.StringID.T(
        default=DEFAULT_STORE_ID,
        help='The GF store to use for forward-calculations.')

    def get_stations(self):
        return self.station_generator.get_stations()

    def get_targets(self):
        stations = self.get_stations()
        lats = num.array([s.lat for s in stations])
        lons = num.array([s.lon for s in stations])

        target = gf.GNSSCampaignTarget(
            lats=lats,
            lons=lons,
            store_id=self.store_id)

        return [target]

    def get_gnss_campaigns(self, engine, sources, tmin=None, tmax=None):
        try:
            resp = engine.process(
                sources,
                self.get_targets(),
                nthreads=0)
        except gf.meta.OutOfBounds:
            logger.warning('Could not calculate GNSS displacements'
                           ' - the GF store\'s extend is too small!')
            return []

        campaigns = [r.campaign for r in resp.static_results()]

        stacked_campaign = campaigns[0]
        stacked_campaign.name = 'Scenario Campaign'
        for camp in campaigns[1:]:
            for ista, sta in enumerate(camp.stations):
                stacked_campaign.stations[ista].north.shift += sta.north.shift
                stacked_campaign.stations[ista].east.shift += sta.east.shift
                stacked_campaign.stations[ista].up.shift += sta.up.shift

        for ista, sta in enumerate(stacked_campaign.stations):
            sta.code = 'SY%02d' % (ista + 1)

        if self.noise_generator is not None:
            self.noise_generator.add_noise(stacked_campaign)

        return [stacked_campaign]

    def dump_data(self, engine, sources, path,
                  tmin=None, tmax=None, overwrite=False):
        path_gnss = op.join(path, 'gnss')
        gf.store.remake_dir(path_gnss, force=overwrite)

        campaigns = self.get_gnss_campaigns(
            engine, sources, tmin, tmax)

        fn = op.join(path_gnss,
                     'campaign-%s.yml' % self.station_generator.network_name)

        with open(fn, 'w') as f:
            for camp in campaigns:
                camp.dump(stream=f)

        return [fn]

    def add_map_artists(self, engine, sources, automap):
        automap.add_gnss_campaign(self.get_gnss_campaigns(engine, sources)[0])
