/**
 * license: GPLv3
 * lodge.reservationsystem
*/
package lodge.reservationsystem;

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.attribute.BasicFileAttributes;
import java.time.ZonedDateTime;

import com.google.gson.stream.JsonReader;
import lodge.data.Address;
import lodge.data.EmailAddress;
import lodge.data.Account;
import lodge.data.KitchenTypeEnum;
import lodge.data.ReservationStatusEnum;
import lodge.reservation.Reservation;

public final class DataRepository {
    // SINGLETON CLASS
    // hard code data store location for storage of
    // account data files on filesystem
    private String directoryPath;
    private static final DataRepository instance = new DataRepository();

    protected final static DataRepository getInstance() {
        return instance;
    }

    public final static void setDataStoreRoot(final String direcoryPath) {
        getInstance().directoryPath = direcoryPath;
    }

    public final static String getPath() {
        return getInstance().directoryPath;
    }

    public final static Reservation Reservation(String type) {
        switch (type) {
            case "HotelReservation":
                return new HotelReservation();
            case "HouseReservation":
                return new HouseReservation();
            case "CabinReservation":
                return new CabinReservation();
        }
        return null;
    }

    public static void WalkFileSystemTree(final AccomodationManager manager, final Path rootDir) throws IOException {
        Files.walkFileTree(rootDir, new SimpleFileVisitor<Path>() {
            @Override
            public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) {
                System.out.println("File: " + file.toAbsolutePath());
                // load account number, and content
                if (attrs.isRegularFile()) {
                    final String namestring = file.getName(file.getNameCount() - 1).toString();
                    if (namestring.endsWith("json")) {
                        if (namestring.startsWith("acc")) { // * load Account *//
                            try {
                                manager.load(file);
                            } catch (final Exception e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
                // load reservation
                return FileVisitResult.CONTINUE;
            }

            @Override
            public FileVisitResult preVisitDirectory(final Path dir, final BasicFileAttributes attrs) {
                System.out.println("Directory: " + dir.toAbsolutePath());
                // prepare to load account number
                return FileVisitResult.CONTINUE;
            }
        });
    }

    public final static Account LoadAccount(final Path file) throws IOException {
        /** @TODO finish loading Account */
        try (BufferedReader in = new BufferedReader(new FileReader(file.toFile(), StandardCharsets.UTF_8));
                JsonReader jsonReader = new JsonReader(in)) {
            jsonReader.beginObject();
            Account ac = new Account();
            while (jsonReader.hasNext()) {
                final String name = jsonReader.nextName();
                switch (name) {
                    case "Account":
                        jsonReader.beginObject();
                        break;
                    case "account_number":
                        ac.setAccount_number(jsonReader.nextString());
                        break;
                    case "phone_number":
                        ac.setPhone_number(jsonReader.nextString());
                        break;
                    case "mailing_address":
                        jsonReader.beginObject();
                        break;
                    case "Address":
                        jsonReader.beginObject();
                        jsonReader.nextName();
                        String mstreet = jsonReader.nextString();
                        jsonReader.nextName();
                        String mcity = jsonReader.nextString();
                        jsonReader.nextName();
                        String mstate = jsonReader.nextString();
                        jsonReader.nextName();
                        String mzip = jsonReader.nextString();
                        jsonReader.endObject();
                        jsonReader.endObject();
                        ac.setMailing_address(new Address(mstreet, mcity, mstate, mzip));
                        break;
                    case "email_address":
                        jsonReader.beginObject();
                        break;
                    case "EmailAddress":
                        jsonReader.beginObject();
                        break;
                    case "email":
                        String s = jsonReader.nextString();
                        s = s != null ? s : "";
                        ac.setEmail_address(new EmailAddress(s));
                        jsonReader.endObject();
                        jsonReader.endObject();
                        break;
                    case "reservations":
                        loadReservationRefList(jsonReader, ac);
                        break;
                    default:
                        System.out.println(name);
                }
            }
            jsonReader.close();
            return ac.getAccount_number().length() > 8 ? ac : null;
        }
    }

    static void loadReservationRefList(JsonReader rdr, Account ac) throws IOException {
        rdr.beginArray();
        while (rdr.hasNext()) {
            rdr.beginObject();
            String reservationType = rdr.nextName();
            rdr.beginObject();
            rdr.nextName();
            String reservationNumber = rdr.nextString();
            loadReservation(ac, reservationType, reservationNumber);
            rdr.endObject();
            rdr.endObject();
        }
        rdr.endArray();
    }

    private static void loadReservation(Account ac, String reservationType,
            String reservationNumber) throws NullPointerException, IOException {
        String filename = String.format("rsv-%s.json", reservationNumber);
        Path basePath = Paths.get(getPath());
        Path resolvedPath = basePath.resolve(filename);

        try (BufferedReader in = new BufferedReader(new FileReader(resolvedPath.toFile(), StandardCharsets.UTF_8))) {
            try (JsonReader jsonReader = new JsonReader(in)) {
                jsonReader.beginObject();
                Reservation rsrv = null;
                try {
                    while (jsonReader.hasNext()) {
                        String name = jsonReader.nextName();
                        switch (name) {
                            case "HotelReservation":
                                jsonReader.beginObject();
                                rsrv = Reservation("HotelReservation");
                                break;
                            case "HouseReservation":
                                jsonReader.beginObject();
                                rsrv = Reservation("HouseReservation");
                                break;
                            case "CabinReservation":
                                jsonReader.beginObject();
                                rsrv = Reservation("CabinReservation");
                                break;
                            case "physical_address":
                                jsonReader.beginObject();
                                jsonReader.nextName();
                                jsonReader.beginObject();
                                jsonReader.nextName();
                                String street = jsonReader.nextString();
                                jsonReader.nextName();
                                String city = jsonReader.nextString();
                                jsonReader.nextName();
                                String state = jsonReader.nextString();
                                jsonReader.nextName();
                                String zip = jsonReader.nextString();
                                jsonReader.endObject();
                                jsonReader.endObject();
                                rsrv.setPhysical_address(new Address(street, city, state, zip));
                                break;
                            case "mailing_address":
                                jsonReader.beginObject();
                                jsonReader.nextName();
                                jsonReader.beginObject();
                                jsonReader.nextName();
                                String mstreet = jsonReader.nextString();
                                jsonReader.nextName();
                                String mcity = jsonReader.nextString();
                                jsonReader.nextName();
                                String mstate = jsonReader.nextString();
                                jsonReader.nextName();
                                String mzip = jsonReader.nextString();
                                jsonReader.endObject();
                                jsonReader.endObject();
                                rsrv.setMailing_address(new Address(mstreet, mcity, mstate, mzip));
                                break;
                            case "reservation_type":
                                jsonReader.nextString();
                                break;
                            case "reservation_number":
                                rsrv.setReservation_number(jsonReader.nextString());
                                break;
                            case "reservation_status":
                                rsrv.setReservation_status(ReservationStatusEnum.valueOf(jsonReader.nextString()));
                                break;
                            case "kitchen":
                                rsrv.setKitchen(KitchenTypeEnum.valueOf(jsonReader.nextString()));
                                break;
                            case "numberOfBeds":
                                rsrv.setNumberOfBeds(Integer.valueOf(jsonReader.nextString()));
                                break;
                            case "numberOfBedRooms":
                                rsrv.setNumberOfBedRooms(Integer.valueOf(jsonReader.nextString()));
                                break;
                            case "numberOfBathRooms":
                                rsrv.setNumberOfBathRooms(Integer.valueOf(jsonReader.nextString()));
                                break;
                            case "numberOfFloors":
                                rsrv.setNumberOfFloors(Integer.valueOf(jsonReader.nextString()));
                                break;
                            case "squareFeet":
                                rsrv.setSquareFeet(Integer.valueOf(jsonReader.nextString()));
                                break;
                            case "price":
                                rsrv.setPrice(Float.valueOf(jsonReader.nextString()));
                                break;
                            case "reservation_start_date":
                                rsrv.setReservation_start_date(ZonedDateTime.parse(jsonReader.nextString()));
                                break;
                            case "reservation_end_date":
                                rsrv.setReservation_end_date(ZonedDateTime.parse(jsonReader.nextString()));
                                break;
                            case "account_number":
                                rsrv.setAccountNumber(jsonReader.nextString());
                                break;

                            default:
                                System.out.println(name);
                        }
                    }

                    ac.add(rsrv);

                } catch (Exception e) {
                    System.out.println(e.toString());
                }

            }

        }

    }
}
