<?php
class Uploader{
    protected $destination;
    protected $max_file_size = 2048000;
    protected $allowed = ['jpeg', 'jpg', 'pjpeg', 'png', 'webp', 'gif'];
    protected static $errors = [];
    protected $new_name;

    public function __construct($upload_folder){
        if(!is_dir($upload_folder) && !is_writable($upload_folder)){
           mkdir($upload_folder, 0777);
        }
        if($upload_folder[strlen($upload_folder) - 1] != '\\'){
             $upload_folder .= '\\';
        }
        $this->destination = $upload_folder;
    }

    /**
     * public function to set the maximum size for the uploaded file
     * @param int $bytes
     * @return bool
     */
    public function setMax($bytes){
       $server_max = self::convertToBytes(ini_get('upload_max_filesize'));
       if($bytes > $server_max){
           self::$errors[] = "file size too large";
           return false;
       }else{
           $this->max_file_size = $bytes;
           return true;
       }
    }
/**
 * public function to do the uploading
 *
 */
    public function upload(){
      $uploaded = current($_FILES);
      $check_upload_error = $this->checkUploadError($uploaded);
      $check_size = $this->checkFileSize($uploaded);
      $check_allowed = $this->checkAllowed($uploaded);

      if($check_upload_error){
         if($check_size){
           if($check_allowed){
               $this->setNewName($uploaded);
               if($this->moveFile($uploaded)){
                   return $this->destination.$this->new_name;
               }else{
                   return false;
               }
           }else{
               return false;
           }
         }else{
             return false;
         }
      }else{
          return false;
      }
    }
    /**
     * public static function for converting to bytes
     * @param $val
     * @return int $val
     */
    public static function convertToBytes($val){
        $val = trim($val);
            $num = strlen($val);
            $last = strtolower($val[$num - 1]);
            if (!empty($last)) {
                if(in_array($last, ['g', 'm', 'k'])){
                    //        remove the trailing none numeric character from $val
                    $val = substr($val, 0, -1);
                    switch ($last){
                        case 'g':
                            $val *= 1024;
                        case 'm':
                            $val *= 1024;
                        case 'k':
                            $val *= 1024;
                    }
                }
            }
        return $val;
    }

    /**
     * public static function for converting from bytes to kilobytes or megabytes
     * @param $bytes
     * @return double
     */
    public static function convertFromBytes($bytes){
        $kilobytes = $bytes / 1024;
        if($kilobytes > 1024){
            return number_format($kilobytes / 1024, 1).'MB';
        }else{
            return number_format($kilobytes, 1);
        }
    }
    /**
     * public function for getting the errors
     * @return array
     */
    public function getErrors(){
        return self::$errors;
    }
//   protected functions
    /**
     * protected function for checking if the uploaded file is allowed
     * @param $file
     * @return bool
     */
    protected function checkAllowed($file){
        $name_parts = pathinfo($file['name']);
        if(in_array($name_parts['extension'], $this->allowed)){
            return true;
        }else{
            self::$errors[] = 'The file type for '.$file['name'].' is not allowed';
            return false;
        }
    }
    /**
     * protected function for checking if there is any error in the file uploaded
     * @param $file
     * @return bool
     */
    protected function checkUploadError($file){
        $upload_error = $file['error'];
        switch ($upload_error) {
            case 0:
                return true;
                break;
            case 1:
            case 2:
                self::$errors[] = $file['name'] . ' is too large.';
                return false;
                break;
            case 3:
                self::$errors[] = $file['name'] . ' was only partially uploaded.';
                return false;
                break;
            case 4:
                self::$errors[] ='no file was selected.';
                return false;
                break;
            default:
                self::$errors[] = 'An error occurred while uploading';
        }
    }
    /**
     * protected function for checking the size of the uploaded file
     * @param $file
     * @return bool
     */
    protected function checkFileSize($file){
       if($file['size'] > $this->max_file_size){
           self::$errors[] = $file['name'] . ' is too large.';
           return false;
       }else{
           return true;
       }
    }
    /**
     * protected function for setting the new file name
     * @param $file
     */
    protected function setNewName($file){
        $no_space = str_replace(' ', '_', $file['name']);
        $this->new_name = $no_space;

    }
    /**
     * protected function for moving the file to the upload destination
     * @param $file
     * @return bool
     */
    protected function moveFile($file){
        $move_file = move_uploaded_file($file['tmp_name'], $this->destination.$this->new_name);
        if($move_file){
            return true;
        }else{
            return false;
        }
    }
}