<?php

if ( !defined( 'ABSPATH' ) ) {

    exit;
}

if ( !class_exists( 'Pricely_Range_Util' ) ) {

    class Pricely_Range_Util {

        private $range_keys;

        public function get_bulk_items( $qty, $items, $ranges ) {

            $bulk_items = array();

            if ( $qty <= 0 ) {

                return $bulk_items;
            }

            $range = $this->get_bulk_qty_range( $qty, $ranges );

            if ( !$range ) {

                return $bulk_items;
            }

            // Get items based no max value range
            if ( !isset( $range[ 'max' ] ) ) {

                foreach ( $items as $key => $item_qty ) {

                    $bulk_items = $this->add_bulk_item_qty( $bulk_items, $key, $range[ 'id' ], $item_qty );
                }

                return $bulk_items;
            }

            // Get items based max value range
            $qty_total = 0;

            foreach ( $items as $key => $item_qty ) {

                if ( $qty_total > $range[ 'max' ] ) {

                    return $bulk_items;
                }

                if ( $qty_total + $item_qty > $range[ 'max' ] ) {

                    $extra_qty = ($qty_total + $item_qty) - $range[ 'max' ];

                    $new_qty = $item_qty - $extra_qty;

                    $bulk_items = $this->add_bulk_item_qty( $bulk_items, $key, $range[ 'id' ], $new_qty );
                } else {

                    $bulk_items = $this->add_bulk_item_qty( $bulk_items, $key, $range[ 'id' ], $item_qty );
                }

                $qty_total += $item_qty;
            }

            return $bulk_items;
        }

        public function process_ranges( $ranges, $add_excluded_ranges ) {

            //Set range keys for processing
            $this->set_range_keys( $ranges );

            return $this->process_min_max( $ranges, $add_excluded_ranges );
        }

        private function get_bulk_qty_range( $qty, $ranges ) {

            $range = false;

            $keys = $this->get_range_keys();

            for ( $i = 0; $i < count( $keys ); $i++ ) {

                $key = $keys[ $i ];

                if ( !isset( $ranges[ $key ] ) ) {

                    return $range;
                }

                $current_range = $ranges[ $key ];

                if ( !isset( $current_range[ 'id' ] ) ) {

                    continue;
                }

                if ( $qty >= $current_range[ 'min' ] ) {

                    $range = $current_range;
                }
            }

            return $range;
        }

        private function process_min_max( $in_ranges, $add_excluded_ranges ) {

            $ranges = array();

            $keys = array_keys( $in_ranges );

            $las_max = 0;

            for ( $i = 0; $i < count( $keys ); $i++ ) {

                $key = $keys[ $i ];

                $range = $in_ranges[ $key ];


                //prevent overlap
                $next_min = $this->get_next_range_min( $i, $in_ranges );

                if ( false !== $next_min && $this->range_has_max( $range ) && $next_min <= $range[ 'max' ] ) {

                    $range[ 'max' ] = $next_min - 1;
                }

                //add the range
                if ( $this->range_has_min( $range ) && $this->range_has_max( $range ) && $this->check_min_max( $range[ 'min' ], $range[ 'max' ] ) ) {

                    $ranges[] = $range;

                    $las_max = $range[ 'max' ];

                    continue;
                }

                //Set min value if not present
                if ( !$this->range_has_min( $range ) && false !== $this->get_prev_range_max( $i, $in_ranges ) ) {

                    $range[ 'min' ] = $this->get_prev_range_max( $i, $in_ranges ) + 1;
                } else if ( !$this->range_has_min( $range ) ) {

                    $range[ 'min' ] = $las_max + 1;
                }

                //Set max value if not present
                if ( !$this->range_has_max( $range ) && false !== $this->get_next_range_min( $i, $in_ranges ) ) {

                    $range[ 'max' ] = $this->get_next_range_min( $i, $in_ranges ) - 1;

                    $las_max = $range[ 'max' ];
                }


                //check for max only
                if ( !$this->range_has_max( $range ) ) {

                    $ranges[] = $range;
                }

                // Check min and max values
                $min = 0;
                $max = 0;

                if ( isset( $range[ 'min' ] ) ) {

                    $min = $range[ 'min' ];
                }

                if ( isset( $range[ 'max' ] ) ) {

                    $max = $range[ 'max' ];
                }

                if ( $this->check_min_max( $min, $max ) ) {

                    $ranges[] = $range;
                }
            }

            if ( $add_excluded_ranges ) {

                return $this->process_excluded_ranges( $ranges );
            }

            return $ranges;
        }

        private function process_excluded_ranges( $in_ranges ) {

            $ranges = array();

            $keys = $this->get_range_keys();

            for ( $i = 0; $i < count( $keys ); $i++ ) {

                $key = $keys[ $i ];

                $range = $in_ranges[ $key ];

                $prev_ex_range = $this->get_excluded_prev_range( $range, $i, $in_ranges );

                if ( false !== $prev_ex_range ) {

                    $ranges[] = $prev_ex_range;
                }

                $ranges[] = $range;
            }

            return $ranges;
        }

        private function get_prev_range_max( $current_index, $in_ranges ) {

            if ( !$this->has_prev_range( $current_index ) ) {

                return false;
            }

            $range = $this->get_prev_range( $current_index, $in_ranges );

            if ( false !== $range && isset( $range[ 'max' ] ) ) {

                return $range[ 'max' ];
            }

            return false;
        }

        private function get_next_range_min( $current_index, $in_ranges ) {

            if ( !$this->has_next_range( $current_index ) ) {

                return false;
            }

            $range = $this->get_next_range( $current_index, $in_ranges );

            if ( false !== $range && isset( $range[ 'min' ] ) ) {

                return $range[ 'min' ];
            }

            return false;
        }

        private function get_prev_range( $current_index, $in_ranges ) {

            if ( !$this->has_prev_range( $current_index ) ) {

                return false;
            }

            $keys = $this->get_range_keys();

            $prev_key = $keys[ $current_index - 1 ];

            if ( isset( $in_ranges[ $prev_key ] ) ) {

                return $in_ranges[ $prev_key ];
            }

            return false;
        }

        private function get_next_range( $current_index, $in_ranges ) {

            if ( !$this->has_next_range( $current_index ) ) {

                return false;
            }

            $keys = $this->get_range_keys();

            $next_key = $keys[ $current_index + 1 ];

            if ( isset( $in_ranges[ $next_key ] ) ) {

                return $in_ranges[ $next_key ];
            }

            return false;
        }

        private function has_prev_range( $current_index ) {

            $keys = $this->get_range_keys();

            if ( isset( $keys[ $current_index - 1 ] ) ) {

                return true;
            }

            return false;
        }

        private function has_next_range( $current_index ) {

            $keys = $this->get_range_keys();

            if ( isset( $keys[ $current_index + 1 ] ) ) {

                return true;
            }

            return false;
        }

        private function range_has_min( $range ) {

            if ( isset( $range[ 'min' ] ) && is_numeric( $range[ 'min' ] ) ) {

                return true;
            }

            return false;
        }

        private function range_has_max( $range ) {

            if ( isset( $range[ 'max' ] ) && is_numeric( $range[ 'max' ] ) ) {

                return true;
            }

            return false;
        }

        private function get_excluded_prev_range( $range, $current_index, $in_ranges ) {

            $current_min = $range[ 'min' ];

            if ( 1 == $current_min ) {

                return false;
            }

            $prev_max = $this->get_prev_range_max( $current_index, $in_ranges );

            $min = 0;

            if ( false === $prev_max ) {

                $min = 1;
            } else {

                $min = $prev_max + 1;
            }

            $max = $current_min - 1;

            if ( $this->check_min_max( $min, $max ) ) {

                return array(
                    'min' => $min,
                    'max' => $max
                );
            }

            return false;
        }

        private function check_min_max( $min, $max ) {

            return ($min > 0 && $max >= $min);
        }

        private function add_bulk_item_qty( $items, $item_key, $range_id, $qty ) {

            if ( $qty <= 0 ) {

                return $items;
            }

            $items[ $item_key ] = array(
                'range_id' => $range_id,
                'qty' => $qty,
            );

            return $items;
        }

        private function set_range_keys( $in_ranges ) {

            $this->range_keys = array_keys( $in_ranges );
        }

        private function get_range_keys() {

            if ( isset( $this->range_keys ) ) {

                return $this->range_keys;
            }

            return false;
        }

    }

}

