"""Datasets for testing.

This module defines a few datasets for testing, covering the whole DAP data
model.

"""
import os
from collections import OrderedDict

import numpy as np

from dapclient.client import open_file
from dapclient.handlers.lib import IterData
from dapclient.model import BaseType, DatasetType, GridType, SequenceType, StructureType

# Note that DAP2 does not support signed bytes (signed 8bits integers).

# A very simple sequence: flat and with no strings. This sequence can be mapped
# directly to a Numpy structured array, and can be easily encoded and decoded
# in the DAP spec.
VerySimpleSequence = DatasetType("VerySimpleSequence")
VerySimpleSequence["sequence"] = SequenceType("sequence")
VerySimpleSequence["sequence"]["byte"] = BaseType("byte")
VerySimpleSequence["sequence"]["int"] = BaseType("int")
VerySimpleSequence["sequence"]["float"] = BaseType("float")
VerySimpleSequence["sequence"].data = np.array(
    [
        (0, 1, 10.0),
        (1, 2, 20.0),
        (2, 3, 30.0),
        (3, 4, 40.0),
        (4, 5, 50.0),
        (5, 6, 60.0),
        (6, 7, 70.0),
        (7, 8, 80.0),
    ],
    dtype=[("byte", np.ubyte), ("int", "i4"), ("float", "f4")],
)


# A nested sequence.
NestedSequence = DatasetType("NestedSequence")
NestedSequence["location"] = SequenceType("location")
NestedSequence["location"]["lat"] = BaseType("lat")
NestedSequence["location"]["lon"] = BaseType("lon")
NestedSequence["location"]["elev"] = BaseType("elev")
NestedSequence["location"]["time_series"] = SequenceType("time_series")
NestedSequence["location"]["time_series"]["time"] = BaseType("time")
NestedSequence["location"]["time_series"]["slp"] = BaseType("slp")
NestedSequence["location"]["time_series"]["wind"] = BaseType("wind")
NestedSequence["location"].data = IterData(
    [
        (1, 1, 1, [(10, 11, 12), (21, 22, 23)]),
        (2, 4, 4, [(15, 16, 17)]),
        (3, 6, 9, []),
        (4, 8, 16, [(31, 32, 33), (41, 42, 43), (51, 52, 53), (61, 62, 63)]),
    ],
    NestedSequence.location,
)


# A simple array with bytes, strings and shorts. These types require special
# encoding for the DODS response.
SimpleArray = DatasetType("SimpleArray")
SimpleArray["byte"] = BaseType("byte", np.arange(5, dtype=np.ubyte))
SimpleArray["string"] = BaseType("string", np.array(["one", "two"]))
SimpleArray["short"] = BaseType("short", np.array(1, dtype="h"))


DODS = os.path.join(os.path.dirname(__file__), "data/rainfall_time_malaysia.cdp.dods")
DAS = os.path.join(os.path.dirname(__file__), "data/rainfall_time_malaysia.cdp.das")
dapper = open_file(DODS, DAS)


# dataset from http://test.opendap.org:8080/dods/dts/D1.asc
D1 = DatasetType("EOSDB.DBO", type="Drifters")
D1["Drifters"] = SequenceType("Drifters")
D1["Drifters"]["instrument_id"] = BaseType("instrument_id")
D1["Drifters"]["location"] = BaseType("location")
D1["Drifters"]["latitude"] = BaseType("latitude")
D1["Drifters"]["longitude"] = BaseType("longitude")
D1.Drifters.data = np.array(
    np.rec.fromrecords(
        list(
            zip(
                [
                    "This is a data test string (pass {}).".format(1 + i * 2)
                    for i in range(5)
                ],
                [
                    "This is a data test string (pass {}).".format(i * 2)
                    for i in range(5)
                ],
                [1000.0, 999.95, 999.80, 999.55, 999.20],
                [999.95, 999.55, 998.75, 997.55, 995.95],
            )
        ),
        names=list(D1.Drifters.keys()),
    )
)


# testing structures
SimpleStructure = DatasetType("SimpleStructure")
SimpleStructure["types"] = StructureType(
    name="types",
    key="value",
    nested=OrderedDict(
        [
            ("string", "bar"),
            ("list", [42, 43]),
            ("array", np.array(1)),
            ("float", 1000.0),
        ]
    ),
)
SimpleStructure["types"]["b"] = BaseType("b", np.array(-10, np.byte))
SimpleStructure["types"]["ub"] = BaseType("ub", np.array(10, np.ubyte))
SimpleStructure["types"]["i32"] = BaseType("i32", np.array(-10, np.int32))
SimpleStructure["types"]["ui32"] = BaseType("ui32", np.array(10, np.uint32))
SimpleStructure["types"]["i16"] = BaseType("i16", np.array(-10, np.int16))
SimpleStructure["types"]["ui16"] = BaseType("ui16", np.array(10, np.uint16))
SimpleStructure["types"]["f32"] = BaseType("f32", np.array(100.0, np.float32))
SimpleStructure["types"]["f64"] = BaseType("f64", np.array(1000.0, np.float64))
SimpleStructure["types"]["s"] = BaseType(
    "s", np.array("This is a data test string (pass 0).")
)
SimpleStructure["types"]["u"] = BaseType("u", np.array("http://www.dods.org"))
SimpleStructure["types"]["U"] = BaseType("U", np.array("test unicode", str))


# test grid
rain = DatasetType("test")
rain["rain"] = GridType("rain")
rain["rain"]["rain"] = BaseType(
    "rain", np.arange(6).reshape(2, 3), dimensions=("y", "x")
)
rain["rain"]["x"] = BaseType("x", np.arange(3), units="degrees_east")
rain["rain"]["y"] = BaseType("y", np.arange(2), units="degrees_north")


# test for ``bounds`` function
bounds = DatasetType("test")
bounds["sequence"] = SequenceType("sequence")
bounds["sequence"]["lon"] = BaseType("lon", axis="X")
bounds["sequence"]["lat"] = BaseType("lat", axis="Y")
bounds["sequence"]["depth"] = BaseType("depth", axis="Z")
bounds["sequence"]["time"] = BaseType("time", axis="T", units="days since 1970-01-01")
bounds["sequence"]["measurement"] = BaseType("measurement")
bounds.sequence.data = np.array(
    np.rec.fromrecords(
        [
            (100, -10, 0, -1, 42),
            (200, 10, 500, 1, 43),
        ],
        names=list(bounds.sequence.keys()),
    )
)


# test for density
ctd = DatasetType("ctd")
ctd["cast"] = SequenceType("cast")
ctd["cast"]["temperature"] = BaseType("temperature")
ctd["cast"]["salinity"] = BaseType("salinity")
ctd["cast"]["pressure"] = BaseType("pressure")
ctd.cast.data = np.array(
    np.rec.fromrecords(
        [
            (21, 35, 0),
            (15, 35, 100),
        ],
        names=list(ctd.cast.keys()),
    )
)

# a simple sequence, simulating a CTD profile
SimpleSequence = DatasetType(
    "SimpleSequence", description="A simple sequence for testing.", nested={"value": 42}
)
SimpleSequence["cast"] = SequenceType("cast")
SimpleSequence["cast"]["id"] = BaseType("id")
SimpleSequence["cast"]["lon"] = BaseType("lon", axis="X")
SimpleSequence["cast"]["lat"] = BaseType("lat", axis="Y")
SimpleSequence["cast"]["depth"] = BaseType("depth", axis="Z")
SimpleSequence["cast"]["time"] = BaseType(
    "time", axis="T", units="days since 1970-01-01"
)
SimpleSequence["cast"]["temperature"] = BaseType("temperature")
SimpleSequence["cast"]["salinity"] = BaseType("salinity")
SimpleSequence["cast"]["pressure"] = BaseType("pressure")
SimpleSequence["cast"].data = np.array(
    np.rec.fromrecords(
        [
            ("1", 100, -10, 0, -1, 21, 35, 0),
            ("2", 200, 10, 500, 1, 15, 35, 100),
        ],
        names=list(SimpleSequence["cast"].keys()),
    )
)
# SimpleSequence["cast"].data = IterData([
#     ("1", 100, -10,   0, -1, 21, 35,   0),
#     ("2", 200,  10, 500,  1, 15, 35, 100),
# ], SimpleSequence.cast)

# a simple grid
SimpleGrid = DatasetType("SimpleGrid", description="A simple grid for testing.")
SimpleGrid["SimpleGrid"] = GridType("SimpleGrid")
SimpleGrid["SimpleGrid"]["SimpleGrid"] = BaseType(
    "SimpleGrid", np.arange(6).reshape(2, 3), dimensions=("y", "x")
)
SimpleGrid["x"] = SimpleGrid["SimpleGrid"]["x"] = BaseType(
    "x", np.arange(3), axis="X", units="degrees_east"
)
SimpleGrid["y"] = SimpleGrid["SimpleGrid"]["y"] = BaseType(
    "y", np.arange(2), axis="Y", units="degrees_north"
)


# a faulty grid
FaultyGrid = DatasetType("FaultyGrid", description="A faulty grid for testing.")
FaultyGrid["FaultyGrid"] = GridType("FaultyGrid")
FaultyGrid["FaultyGrid"]["FaultyGrid"] = BaseType(
    "FaultyGrid", np.arange(6).reshape(2, 3), dimensions=("y", "x")
)
FaultyGrid["x"] = FaultyGrid["FaultyGrid"]["x"] = BaseType(
    "x", np.arange(3), axis="X", code=1
)
FaultyGrid["y"] = FaultyGrid["FaultyGrid"]["y"] = BaseType(
    "y", np.arange(2), axis="Y", code=np.int32([])
)
