var express    = require('express');
var app        = express();

const bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({ extended: false}));
app.use(bodyParser.json());


/*

    Flight:
        "name"
        "take-off time"
        "priority"
        "status"

//*/

function uuidv4() {
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
        return v.toString(16);
    });
}



function Flight( name, boardId, unitId ) {
    this.assignedAltitude = 0;
    this.assignedSpeed    = 0;
    this.id               = uuidv4();
    this.boardId          = boardId;
    this.name             = name;
    this.status           = "unknown";
    this.takeoffTime      = -1;
    this.unitId           = parseInt( unitId );
}

Flight.prototype.getData = function() {
    return {
        "assignedAltitude" : this.assignedAltitude,
        "assignedSpeed"    : this.assignedSpeed,
        "id"               : this.id,
        "boardId"          : this.boardId,
        "name"             : this.name,
        "status"           : this.status,
        "takeoffTime"      : this.takeoffTime,
        "unitId"           : this.unitId
    };
}


Flight.prototype.setAssignedAltitude = function( assignedAltitude ) {

    if ( isNaN( assignedAltitude ) ) {
        return "Altitude must be a number"
    }

    this.assignedAltitude = parseInt( assignedAltitude );

    return true;

}


Flight.prototype.setAssignedSpeed = function( assignedSpeed ) {

    if ( isNaN( assignedSpeed ) ) {
        return "Speed must be a number"
    }

    this.assignedSpeed = parseInt( assignedSpeed );

    return true;

}


Flight.prototype.setOrder = function( order ) {

    this.order = order;

    return true;

}


Flight.prototype.setStatus = function( status ) {

    if ( [ "unknown", "checkedin", "readytotaxi", "clearedtotaxi", "halted", "terminated" ].indexOf( status ) < 0 ) {
        return "Invalid status";
    }

    this.status = status;

    return true;

}


Flight.prototype.setTakeoffTime = function( takeoffTime ) {

    if ( takeoffTime === "" || takeoffTime === -1 ) {
        this.takeoffTime = -1;
    }

    if ( isNaN( takeoffTime ) ) {
        return "Invalid takeoff time"
    }

    this.takeoffTime = parseInt( takeoffTime );

    return true;

}



function ATCDataHandler( data ) {
    this.data = data;
}

ATCDataHandler.prototype.addFlight = function( flight ) {

    if ( flight instanceof Flight === false ) {
        throw new Error( "Given flight is not an instance of Flight" );
    }

    this.data.flights[ flight.id ] = flight;

}


ATCDataHandler.prototype.deleteFlight = function( flightId ) {
    delete this.data.flights[ flightId ];
}


ATCDataHandler.prototype.getFlight = function( flightId ) {
    return this.data.flights[ flightId ] || false;
}


ATCDataHandler.prototype.getFlights = function() {
    return this.data.flights;
}


const dataHandler = new ATCDataHandler( {
    "flights": {}
} );



/**************************************************************************************************************/
//  Endpoints
/**************************************************************************************************************/


app.get( "/flight", ( req, res ) => {

    let flights = Object.values( dataHandler.getFlights() );

    if ( flights && req.query.boardId ) {
        
        flights = flights.reduce( ( acc, flight ) => {
            if ( flight.boardId === req.query.boardId ) {
                acc[ flight.id ] = flight;
            }
            return acc;
        }, {} );

    }

    res.json( flights );

});


app.patch( "/flight/:flightId", ( req, res ) => {

    const flightId = req.params.flightId;
    const flight   = dataHandler.getFlight( flightId );

    if ( !flight ) {
        res.status( 400 ).send( `Unrecognised flight ID (given: "${req.params.flightId}")` );
    }

    if ( req.body.hasOwnProperty( "assignedAltitude" ) ) {

        const altitudeChangeSuccess = flight.setAssignedAltitude( req.body.assignedAltitude );

        if ( altitudeChangeSuccess !== true ) {
            res.status( 400 ).send( altitudeChangeSuccess );
        }

    }

    if ( req.body.hasOwnProperty( "assignedSpeed" ) ) {

        const speedChangeSuccess = flight.setAssignedSpeed( req.body.assignedSpeed );

        if ( speedChangeSuccess !== true ) {
            res.status( 400 ).send( speedChangeSuccess );
        }

    }

    if ( req.body.status ) {

        const statusChangeSuccess = flight.setStatus( req.body.status );

        if ( statusChangeSuccess !== true ) {
            res.status( 400 ).send( statusChangeSuccess );
        }

    }

    if ( req.body.hasOwnProperty( "takeoffTime" ) ) {

        const takeoffChangeSuccess = flight.setTakeoffTime( req.body.takeoffTime );

        if ( takeoffChangeSuccess !== true ) {
            res.status( 400 ).send( takeoffChangeSuccess );
        }

    }

    res.json( flight.getData() );

});


app.post( "/flight/order", ( req, res ) => {

    if ( !req.body.boardId ) {
        res.status( 400 ).send( "Invalid/missing boardId" );
    }

    if ( !req.body.order || !Array.isArray( req.body.order ) ) {
        res.status( 400 ).send( "Invalid/missing boardId" );
    }

    req.body.order.forEach( ( flightId, i ) => {

        dataHandler.getFlight( flightId ).setOrder( i );

    });

    res.send( "" );

});


app.post( "/flight", ( req, res ) => {

    if ( !req.body.boardId ) {
        res.status( 400 ).send( "Invalid/missing boardId" );
    }

    if ( !req.body.name ) {
        res.status( 400 ).send( "Invalid/missing flight name" );
    }

    if ( !req.body.unitId || isNaN( req.body.unitId ) ) {
        res.status( 400 ).send( "Invalid/missing unitId" );
    }

    const flight = new Flight( req.body.name, req.body.boardId, req.body.unitId );

    dataHandler.addFlight( flight );

    res.status( 201 );

    res.json( flight.getData() );

});


app.delete( "/flight/:flightId", ( req, res ) => {

    const flight = dataHandler.getFlight( req.params.flightId );

    if ( !flight ) {
        res.status( 400 ).send( `Unrecognised flight ID (given: "${req.params.flightId}")` );
    }

    dataHandler.deleteFlight( req.params.flightId );

    res.status( 204 ).send( "" );

});


module.exports = app;