<?php /*

 Composr
 Copyright (c) ocProducts, 2004-2016

 See text/EN/licence.txt for full licencing information.


 NOTE TO PROGRAMMERS:
   Do not edit this file. If you need to make changes, save your changed file to the appropriate *_custom folder
   **** If you ignore this advice, then your website upgrades (e.g. for bug fixes) will likely kill your changes ****

*/

/**
 * @license    http://opensource.org/licenses/cpal_1.0 Common Public Attribution License
 * @copyright  ocProducts Ltd
 * @package    core_fields
 */

/**
 * Hook class.
 */
class Hook_fields_isbn
{
    // ==============
    // Module: search
    // ==============

    /**
     * Get special Tempcode for inputting this field.
     *
     * @param  array $field The field details
     * @return ?array Specially encoded input detail rows (null: nothing special)
     */
    public function get_search_inputter($field)
    {
        return null;
    }

    /**
     * Get special SQL from POSTed parameters for this field.
     *
     * @param  array $field The field details
     * @param  integer $i We're processing for the ith row
     * @param string $table_alias Table alias for catalogue entry table
     * @return ?array Tuple of SQL details (array: extra trans fields to search, array: extra plain fields to search, string: an extra table segment for a join, string: the name of the field to use as a title, if this is the title, extra WHERE clause stuff) (null: nothing special)
     */
    public function inputted_to_sql_for_search($field, $i, $table_alias = 'r')
    {
        return null;
    }

    // ===================
    // Backend: fields API
    // ===================

    /**
     * Get some info bits relating to our field type, that helps us look it up / set defaults.
     *
     * @param  ?array $field The field details (null: new field)
     * @param  ?boolean $required Whether a default value cannot be blank (null: don't "lock in" a new default value) (may be passed as false also if we want to avoid "lock in" of a new default value, but in this case possible cleanup of $default may still happen where appropriate)
     * @param  ?string $default The given default value as a string (null: don't "lock in" a new default value) (blank: only "lock in" a new default value if $required is true)
     * @return array Tuple of details (row-type,default-value-to-use,db row-type)
     */
    public function get_field_value_row_bits($field, $required = null, $default = null)
    {
        return array('short_unescaped', $default, 'short');
    }

    /**
     * Convert a field value to something renderable.
     *
     * @param  array $field The field details
     * @param  mixed $ev The raw value
     * @return mixed Rendered field (Tempcode or string)
     */
    public function render_field_value($field, $ev)
    {
        if (is_object($ev)) {
            $ev = $ev->evaluate();
        }

        if ($ev == '') {
            return '';
        }

        return hyperlink('https://www.bookfinder.com/search/?isbn=' . urlencode($ev) . '&mode=isbn&st=sr&ac=qr', $ev, true, true);
    }

    // ======================
    // Frontend: fields input
    // ======================

    /**
     * Get form inputter.
     *
     * @param  string $_cf_name The field name
     * @param  string $_cf_description The field description
     * @param  array $field The field details
     * @param  ?string $actual_value The actual current value of the field (null: none)
     * @param  boolean $new Whether this is for a new entry
     * @return ?Tempcode The Tempcode for the input field (null: skip the field - it's not input)
     */
    public function get_field_inputter($_cf_name, $_cf_description, $field, $actual_value, $new)
    {
        if (is_null($actual_value)) {
            $actual_value = ''; // Plug anomaly due to unusual corruption
        }
        $input_name = empty($field['cf_input_name']) ? ('field_' . strval($field['id'])) : $field['cf_input_name'];
        return form_input_codename($_cf_name, $_cf_description, $input_name, $actual_value, $field['cf_required'] == 1);
    }

    /**
     * Find the posted value from the get_field_inputter field
     *
     * @param  boolean $editing Whether we were editing (because on edit, it could be a fractional edit)
     * @param  array $field The field details
     * @param  ?string $upload_dir Where the files will be uploaded to (null: do not store an upload, return null if we would need to do so)
     * @param  ?array $old_value Former value of field (null: none)
     * @return ?string The value (null: could not process)
     */
    public function inputted_to_field_value($editing, $field, $upload_dir = 'uploads/catalogues', $old_value = null)
    {
        $id = $field['id'];
        $tmp_name = 'field_' . strval($id);
        $value = post_param_string($tmp_name, $editing ? STRING_MAGIC_NULL : '');
        
        if (option_value_from_field_array($field, 'strict_isbn_validation', 'off') == 'on') {
            if (!$this->is_valid_isbn($value)) {
                warn_exit(do_lang_tempcode('javascript:NOT_VALID_ISBN', $value));
            }
        }
        
        return $value;
    }
    
    /**
     * Check if a provided code is valid ISBN-10 or ISBN-13.
     * 
     * @param  string $isbn The ISBN code to check
     * @return boolean Whether the ISBN code is valid
     */
    protected function is_valid_isbn($isbn) 
    {
        // Remove hyphens and spaces from the ISBN; we will not consider these in validation
        $clean_isbn = str_replace(array('-', ' '), '', $isbn);
        
        // Check if the ISBN is 10 or 13 digits long
        if ((strlen($clean_isbn) == 10) || (strlen($clean_isbn) == 13)) {
            // If it's 10 digits, validate using the ISBN-10 algorithm
            if (strlen($clean_isbn) == 10) {
                $checksum = 0;
                for ($i = 0; $i < 9; $i++) {
                    $checksum += (10 - $i) * intval($clean_isbn[$i]);
                }
                
                $_last_digit = strtoupper($clean_isbn[9]);
                $last_digit = ($_last_digit === 'X') ? 10 : intval($_last_digit);
                
                return ($checksum + $last_digit) % 11 == 0;
            }
            
            // If it's 13 digits, validate using the ISBN-13 algorithm
            if (strlen($clean_isbn) == 13) {
                $checksum = 0;
                for ($i = 0; $i < 12; $i++) {
                    $factor = ($i % 2 == 0) ? 1 : 3;
                    $checksum += $factor * intval($clean_isbn[$i]);
                }
                
                return $checksum % 10 == 0;
            }
        }
        
        return false; // Invalid ISBN length
    }
}
