app/Repositories/VehicleRepositoryEloquent.php
<?php

namespace App\Repositories;

use Prettus\Repository\Eloquent\BaseRepository;
use Prettus\Repository\Criteria\RequestCriteria;
use App\Repositories\VehicleRepository;
use App\Entities\Vehicle;
use Carbon\Carbon;
use Illuminate\Support\Facades\Auth;
use App\Entities\Gps;
use App\Entities\Part;
use App\Entities\TireSensor;
use Log;

class VehicleRepositoryEloquent extends BaseRepository implements VehicleRepository
{

    protected $rules = [
        'model_vehicle_id'   => 'required',
        'cost'      => 'min:3|required',
        ];

    public function model()
    {
        return Vehicle::class;
    }

    public function boot()
    {
        $this->pushCriteria(app(RequestCriteria::class));
    }
    
    public function results($filters = [])
    {
        $filters['cost'] = empty($filters['cost']) ? "" : HelperRepository::money($filters['cost']);
        
        $vehicles = $this->scopeQuery(function ($query) use ($filters) {

            $query = $query->select('vehicles.*', 'models.name as model-vehicle');
            $query = $query->leftJoin('models', 'vehicles.model_vehicle_id', '=', 'models.id');
            
            if (!empty($filters['model-vehicle'])) {
                $query = $query->where('models.name', 'like', '%'.$filters['model-vehicle'].'%');
            }
            if (!empty($filters['number'])) {
                $query = $query->where('vehicles.number', 'like', '%'.$filters['number'].'%');
            }
            if (!empty($filters['cost'])) {
                $query = $query->where('vehicles.cost', 'like', '%'.$filters['cost'].'%');
            }

            $query = $query->where('vehicles.company_id', Auth::user()['company_id']);
            if ($filters['sort'] == 'model_vehicle') {
                $sort = 'models.name';
            } else {
                $sort = 'vehicles.'.$filters['sort'];
            }
            $query = $query->orderBy($sort, $filters['order']);
            
            return $query;
        })->paginate($filters['paginate']);
        
        return $vehicles;
    }
    
    public function hasReferences($idVehicle)
    {
        $vehicle = $this->find($idVehicle);
        $countReferences = $vehicle->entries()->count();
        $countReferences += $vehicle->trips()->count();
        
        if ($countReferences > 0) {
            return true;
        }
        return false;
    }
    
    public static function getVehicles($optionalChoice = false)
    {
        $vehicles = Vehicle::where('vehicles.company_id', Auth::user()['company_id'])
                                ->lists('number', 'id');
        
        if ($optionalChoice) {
            $vehicles->prepend("", "");
        }
                                
        return $vehicles;
    }
    
    public static function getVehiclesLastPlace($vehicle_id = null)
    {
        $sub = Gps::select(\DB::raw('MAX(id) as id'))
            ->groupBy('vehicle_id');
        $prefix = \DB::getTablePrefix();

        $vehicles = Gps::select('gps.id', 'vehicle_id', 'latitude', 'longitude', 'geofence')
            ->join(\DB::raw("({$sub->toSql()}) as ".$prefix."gps2"), 'gps.id', '=', 'gps2.id')
            ->join('vehicles', 'vehicles.id', '=', 'gps.vehicle_id')
            ->where('vehicles.company_id', Auth::user()['company_id']);
        if (!empty($vehicle_id)) {
            $vehicles = $vehicles->where('vehicle_id', $vehicle_id);
        }
        $vehicles = $vehicles->groupBy('vehicle_id')
                            ->get();
        
        $vehicles = self::getVehiclesGeofence($vehicles);
        
        return $vehicles;
    }
    
    private static function isVehicleInGeofence($vehiclePoint, $geofence)
    {
        $distance = sqrt(
            pow(($vehiclePoint['latitude'] - $geofence['latitude']), 2)
            + pow(($vehiclePoint['longitude'] - $geofence['longitude']), 2)
        );
    
        return $distance <= $geofence['radius'];
    }
    
    private static function getVehiclesGeofence($vehicles)
    {
        if (!empty($vehicles)) {
            foreach ($vehicles as $index => $vehicle) {
                if (empty($vehicle['geofence'])) {
                    $vehicles[$index]['in_geofence'] = true;
                } else {
                    $geofenceJson = json_decode($vehicle['geofence'], true);
                    $vehicles[$index]['in_geofence'] = self::isVehicleInGeofence($vehicle, $geofenceJson);
                }
            }
        }
        return $vehicles;
    }
    
    public static function getVehiclesStatistics()
    {
        $vehicles['in_use']['color'] = '#3871cf';
        $vehicles['in_use']['result'] = Vehicle::join('trips', 'vehicles.id', '=', 'trips.vehicle_id')
                ->where('vehicles.company_id', Auth::user()['company_id'])
                ->where('trips.pickup_date', '<=', Carbon::now())
                ->where(function ($query) {
                    $query->where('deliver_date', '>', Carbon::now())
                          ->orWhereNull('deliver_date');
                })
                ->count();
            
        $vehicles['maintenance']['color'] = '#cf7138';
        $vehicles['maintenance']['result'] = Vehicle::join('entries', 'vehicles.id', '=', 'entries.vehicle_id')
                ->join('types', 'types.id', '=', 'entries.entry_type_id')
                ->where('vehicles.company_id', Auth::user()['company_id'])
                ->where('types.entity_key', 'vehicle')
                ->where('types.name', 'repair')
                ->where('entries.datetime_ini', '<=', Carbon::now())
                ->where(function ($query) {
                    $query->where('datetime_end', '>', Carbon::now())
                          ->orWhereNull('datetime_end');
                })
                ->count();
        
        $vehicles['available']['color'] = '#38cf71';
        $vehiclesOff = $vehicles['maintenance']['result'] + $vehicles['in_use']['result'];
        $vehicles['available']['result'] = Vehicle::where('vehicles.company_id', Auth::user()['company_id'])
                                            ->count() - $vehiclesOff;
        return $vehicles;
    }
    
    public function setInputs($inputs)
    {
        if (!empty($inputs['geofence_name']) &&
            !empty($inputs['geofence_radius']) && is_numeric($inputs['geofence_radius']) &&
            !empty($inputs['geofence_latitude']) && is_numeric($inputs['geofence_latitude']) &&
            !empty($inputs['geofence_longitude']) && is_numeric($inputs['geofence_longitude'])
        ) {
            $inputs['geofence'] = '{"latitude": '.$inputs['geofence_latitude'].','.
                                    '"longitude": '.$inputs['geofence_longitude'].', '.
                                    '"radius": '.$inputs['geofence_radius'].', '.
                                    '"transitionType": 2, '.
                                    '"notification": { "text": "'.$inputs['geofence_name'].'"} }';
        } else {
            unset($inputs['geofence']);
        }

        $inputs['cost'] = HelperRepository::money($inputs['cost']);
        if ($inputs['cost'] == "0.00") {
            $inputs['cost'] = 0;
        }
        
        $inputs['entity_key'] = "vehicle";
        
        return $inputs;
    }
    
    public function getLocalizationData($idVehicle)
    {
        $localizationData = Gps::where('vehicle_id', $idVehicle)
            ->orderBy('id', 'desc')
            ->first();
        
        return $localizationData;
    }
    
    public function getTireAndSensorData($inputs)
    {
        $tire = Part::join('types', 'parts.part_type_id', '=', 'types.id')
            ->where('parts.position', $inputs['position'])
            ->where('parts.vehicle_id', $inputs['vehicle_id'])
            ->where('parts.company_id', Auth::user()['company_id'])
            ->where('types.name', 'tire')
            ->first();

        $objTire = new \stdClass();
        
        if (!empty($tire)) {
            $sensor = TireSensor::join('parts', 'tire_sensor.part_id', '=', 'parts.id')
                ->join('types', 'parts.part_type_id', '=', 'types.id')
                ->where('parts.part_id', $tire->id)
                ->where('parts.position', $inputs['position'])
                ->where('parts.vehicle_id', $inputs['vehicle_id'])
                ->where('parts.company_id', Auth::user()['company_id'])
                ->where('types.name', 'sensor')
                ->orderBy('parts.id', 'desc')
                ->first();
            
            $objTire->position = HelperRepository::manageEmptyValue($tire->position);
            $objTire->number = HelperRepository::manageEmptyValue($tire->number);
            $objTire->model = HelperRepository::manageEmptyValue($tire->partModel->name);
            $objTire->lifecycle = HelperRepository::manageEmptyValue($tire->lifecycle);
            $objTire->miliage = HelperRepository::manageEmptyValue($tire->miliage);
            $objTire->part_id = $tire->id;
        } else {
            $objTire->part_id = "";
            $objTire->position = "";
            $objTire->number = "";
            $objTire->model = "";
            $objTire->lifecycle = "";
            $objTire->miliage = "";
        }

        if (!empty($sensor)) {
            $objTire->temperature = HelperRepository::manageEmptyValue($sensor->temperature);
            $objTire->pressure = HelperRepository::manageEmptyValue($sensor->pressure);
            $objTire->battery = HelperRepository::manageEmptyValue($sensor->battery);
            $objTire->sensorNumber = HelperRepository::manageEmptyValue($sensor->number);
        } else {
            $objTire->temperature = "";
            $objTire->pressure = "";
            $objTire->battery = "";
            $objTire->sensorNumber = "";
        }
        return $objTire;
    }
    
    protected function setTiresColor($tiresData, $sensor, $objTire)
    {
        if (!empty($tiresData['parts'][$sensor->part_id])
            && ($sensor->pressure > $tiresData['dangerMaxPressure']
                || $sensor->pressure < $tiresData['dangerMinPressure']
                || $sensor->temperature > (int)config('app.tires_danger_temperature'))
            ) {
                $objTire->color = "red";
        } elseif (!empty($tiresData['parts'][$sensor->part_id])
                && ($sensor->pressure > $tiresData['warningMaxPressure']
                    || $sensor->pressure < $tiresData['warningMinPressure']
                    || $sensor->temperature > (int)config('app.tires_warning_temperature'))
                ) {
            $objTire->color = "yellow";
        } else {
            $objTire->color = "green";
        }
    
            return $objTire;
    }
    
    protected function getTiresWarningAndDanger($sensorsIds)
    {
    

//         $sensors = \DB::select('part_id', \DB::raw('AVG(temperature) as avg_temperature'))
//             ->from(

//                 TireSensor::select('part_id', 'temperature')

//                 ) as 't'
//             ->groupBy('part_id')
//             ->get();

        $sensorsReturn = [];
    
        $warningPressure = ((int)config('app.tires_warning_pressure_percentage') *
            (int)config('app.tires_ideal_pressure')) / 100;
    
        $sensorsReturn['warningMinPressure'] = (int)config('app.tires_ideal_pressure') - $warningPressure;
        $sensorsReturn['warningMaxPressure'] = (int)config('app.tires_ideal_pressure') + $warningPressure;

        $dangerPressure = ((int)config('app.tires_danger_pressure_percentage') *
            (int)config('app.tires_ideal_pressure')) / 100;

            $sensorsReturn['dangerMinPressure'] = (int)config('app.tires_ideal_pressure') - $dangerPressure;
            $sensorsReturn['dangerMaxPressure'] = (int)config('app.tires_ideal_pressure') + $dangerPressure;
                 
//         $sensors = \DB::select(\DB::raw('
//             select part_id, avg(temperature) as avg_temperature, avg(pressure) as avg_pressure
//             from (

//                 select part_id, temperature, pressure
//                 from tire_sensor
//                 where (
//                     select count(*) from tire_sensor as p
//                     where p.part_id = tire_sensor.part_id and p.created_at >= tire_sensor.created_at
//                     and p.part_id in ('.implode(',',$parts).')
//                     ) <= 3
//             ) as t
//             group by part_id'));
    
        $sensors = TireSensor::select('part_id', 'temperature as avg_temperature', 'pressure as avg_pressure')
            ->whereIn('tire_sensor.id', $sensorsIds)
            ->get();

        if (!empty($sensors)) {
            foreach ($sensors as $sensor) {
                $sensorsReturn['parts'][$sensor->part_id] = $sensor;
            }
        }

        return $sensorsReturn;
    }
}
