import collections
import csv
from collections import namedtuple
from dataclasses import dataclass


@dataclass
class DataClassRow:
    __slots__ = ['route', 'date', 'daytype', 'rides']
    route: str
    date: str
    daytype: str
    rides: int


class BasicRow:
    def __init__(self, route, date, daytype, rides):
        self.route = route
        self.date = date
        self.daytype = daytype
        self.rides = rides


class SlotsRow:
    __slots__ = ['route', 'date', 'daytype', 'rides']

    def __init__(self, route, date, daytype, rides):
        self.route = route
        self.date = date
        self.daytype = daytype
        self.rides = rides


TupleRow = namedtuple('Row', ['route', 'date', 'daytype', 'rides'])


def read_rides(filename, readfunc):
    """Read the bus ride data using a conversion function."""
    with open(filename) as f:
        rows = csv.reader(f)
        next(rows)
        return [readfunc(row) for row in rows]


def read_rides_as_tuples(filename):
    return read_rides(filename, row_to_tuple)


def read_rides_as_dicts(filename):
    return read_rides(filename, row_to_dict)


def read_rides_as_namedtuples(filename):
    return read_rides(filename, row_to_namedtuple)


def read_rides_as_classes(filename):
    return read_rides(filename, row_to_class)


def read_rides_as_slotsclasses(filename):
    return read_rides(filename, row_to_slotsclass)


def read_rides_as_dataclasses(filename):
    return read_rides(filename, row_to_dataclass)


class RideData(collections.abc.Sequence):
    def __init__(self):
        self.routes = []
        self.dates = []
        self.daytypes = []
        self.numrides = []

    def __len__(self):
        return len(self.routes)

    def __getitem__(self, index):
        if not isinstance(index, slice):
            return dict(route=self.routes[index],
                        date=self.dates[index],
                        daytype=self.daytypes[index],
                        rides=self.numrides[index])
        value = RideData()
        value.routes = self.routes[index]
        value.dates = self.dates[index]
        value.daytypes = self.daytypes[index]
        value.numrides = self.numrides[index]
        return value

    def append(self, d):
        self.routes.append(d['route'])
        self.dates.append(d['date'])
        self.daytypes.append(d['daytype'])
        self.numrides.append(d['rides'])


def read_rides_as_columns(filename):
    """Read the bus ride data into 4 lists, one per column."""
    data = RideData()
    with open(filename) as f:
        rows = csv.reader(f)
        next(rows)
        for row in rows:
            data.append(dict(route=row[0],
                             date=row[1],
                             daytype=row[2],
                             rides=int(row[3])))
    return data


def row_to_tuple(row):
    return (row[0], row[1], row[2], int(row[3]))


def row_to_dict(row):
    return dict(route=row[0], date=row[1], daytype=row[2], rides=int(row[3]))


def row_to_namedtuple(row):
    return TupleRow(row[0], row[1], row[2], int(row[3]))


def row_to_class(row):
    return BasicRow(row[0], row[1], row[2], int(row[3]))


def row_to_slotsclass(row):
    return SlotsRow(row[0], row[1], row[2], int(row[3]))


def row_to_dataclass(row):
    return DataClassRow(route=row[0], date=row[1],
                        daytype=row[2], rides=int(row[3]))


if __name__ == '__main__':
    import sys
    import tracemalloc
    method = sys.argv[1]
    methods = {
        'tuple': row_to_tuple,
        'dict': row_to_dict,
        'namedtuple': row_to_namedtuple,
        'class': row_to_class,
        'slots': row_to_slotsclass,
        'dataclass': row_to_dataclass,
    }
    if method not in methods:
        print("unknown method")
        sys.exit(-1)
    tracemalloc.start()
    rows = read_rides('Data/ctabus.csv', methods[method])
    print('Memory Use: Current %d, Peak %d;' % tracemalloc.get_traced_memory(),
          'Method:', method)
