<?php
namespace Veno\QrCdr\Lib;

use Exception;

/**
 * QR Code Core Generator (Private Fork for Veno QrCdr)
 *
 * Main entry point class for generating QR Code objects.
 * Contains version, width, and final data matrix.
 *
 * @package   QrCdr
 * @version   1.1.4-patched
 */

if ( ! defined( 'ABSPATH' ) && ! defined( 'QRCDR_OPEN_ACCESS' ) ) {
	exit;
}

class QrcdrCode {

	public $version;
	public $width;
	public $data;

	/**
	 * Encode data from QrcdrInput applying a mask.
	 *
	 * @param QrcdrInput $input Data input.
	 * @param int     $mask  Mask ID (-1 for auto).
	 * @return self|null
	 * @throws Exception
	 */
	public function encodeMask( QrcdrInput $input, $mask ) {
		if ( 0 > $input->getVersion() || QRCDR_QRSPEC_VERSION_MAX < $input->getVersion() ) {
			throw new Exception( 'Invalid QR Code version.' );
		}
		if ( QRCDR_ECLEVEL_H < $input->getErrorCorrectionLevel() ) {
			throw new Exception( 'Invalid Error Correction Level.' );
		}

		$raw_code = new QrcdrRawCode( $input );

		$version = $raw_code->version;
		$width   = QrcdrSpec::getWidth( $version );
		$frame   = QrcdrSpec::newFrame( $version );

		// Fill the frame (QrcdrFrameFiller logic must be included)
		$filler  = new QrcdrFrameFiller( $width, $frame );

		if ( null === $filler ) {
			return null;
		}

		// Interleave data and ECC
		for ( $i = 0; $i < $raw_code->dataLength + $raw_code->eccLength; $i++ ) {
			$code = $raw_code->getCode();
			$bit  = 0x80;
			for ( $j = 0; $j < 8; $j++ ) {
				$addr = $filler->next();
				$filler->setFrameAt( $addr, 0x02 | ( ( $bit & $code ) != 0 ) );
				$bit = $bit >> 1;
			}
		}

		unset( $raw_code );

		// Remainder bits
		$remainder_bits = QrcdrSpec::getRemainder( $version );
		for ( $i = 0; $i < $remainder_bits; $i++ ) {
			$addr = $filler->next();
			$filler->setFrameAt( $addr, 0x02 );
		}

		$frame = $filler->frame;
		unset( $filler );

		// Apply mask
		$mask_obj = new QrcdrMask();
		if ( 0 > $mask ) {
			if ( QRCDR_FIND_BEST_MASK ) {
				$masked_frame = $mask_obj->mask( $width, $frame, $input->getErrorCorrectionLevel() );
			} else {
				$masked_frame = $mask_obj->makeMask( $width, $frame, ( intval( QRCDR_DEFAULT_MASK ) % 8 ), $input->getErrorCorrectionLevel() );
			}
		} else {
			$masked_frame = $mask_obj->makeMask( $width, $frame, $mask, $input->getErrorCorrectionLevel() );
		}

		if ( null === $masked_frame ) {
			return null;
		}

		$this->version = $version;
		$this->width   = $width;
		$this->data    = $masked_frame;

		return $this;
	}

	/**
	 * Encode input with auto-mask.
	 */
	public function encodeInput( QrcdrInput $input ) {
		return $this->encodeMask( $input, -1 );
	}

	/**
	 * Encode 8-bit string.
	 */
	public function encodeString8bit( $string, $version, $level ) {
		if ( null === $string ) {
			throw new Exception( 'Input string cannot be empty!' );
		}

		$qr_input = new QrcdrInput( $version, $level );
		if ( null === $qr_input ) {
			return null;
		}

		$return_code = $qr_input->append( QRCDR_MODE_8, strlen( $string ), str_split( $string ) );
		if ( 0 > $return_code ) {
			unset( $qr_input );
			return null;
		}
		return $this->encodeInput( $qr_input );
	}

	/**
	 * Encode string with best mode.
	 */
	public function encodeString( $string, $version, $level, $hint, $case_sensitive ) {
		if ( QRCDR_MODE_8 !== $hint && QRCDR_MODE_KANJI !== $hint ) {
			throw new Exception( 'Invalid mode hint.' );
		}

		$qr_input = new QrcdrInput( $version, $level );
		if ( null === $qr_input ) {
			return null;
		}

		$return_code = QrcdrSplit::splitStringToQRinput( $string, $qr_input, $hint, $case_sensitive );
		if ( 0 > $return_code ) {
			return null;
		}

		return $this->encodeInput( $qr_input );
	}

	// --- STATIC FACTORY METHODS ---

	/**
	 * Generate PNG.
	 * (Note: Uses the deprecated encodePNG which handles the output buffering)
	 */
	public static function png( $text, $outfile = false, $level = QRCDR_ECLEVEL_L, $size = 3, $margin = 4, $save_and_print = false, $back_color = 0xFFFFFF, $fore_color = 0x000000 ) {
		$encoder = QrcdrEncode::factory( $level, $size, $margin, $back_color, $fore_color );
		return $encoder->encodePNG( $text, $outfile, $save_and_print );
	}

	/**
	 * Generate Text matrix.
	 */
	public static function text( $text, $outfile = false, $level = QRCDR_ECLEVEL_L, $size = 3, $margin = 4 ) {
		$encoder = QrcdrEncode::factory( $level, $size, $margin );
		return $encoder->encode( $text, $outfile );
	}

	/**
	 * Generate EPS.
	 */
	public static function eps( $text, $outfile = false, $level = QRCDR_ECLEVEL_L, $size = 3, $margin = 4, $save_and_print = false, $back_color = 0xFFFFFF, $fore_color = 0x000000, $cmyk = false ) {
		$encoder = QrcdrEncode::factory( $level, $size, $margin, $back_color, $fore_color, $cmyk );
		return $encoder->encodeEPS( $text, $outfile, $save_and_print );
	}

	/**
	 * Generate SVG.
	 */
	public static function svg( $text, $outfile = false, $level = QRCDR_ECLEVEL_L, $size = 3, $margin = 4, $save_and_print = false, $back_color = 0xFFFFFF, $fore_color = 0x000000 ) {
		$encoder = QrcdrEncode::factory( $level, $size, $margin, $back_color, $fore_color );
		return $encoder->encodeSVG( $text, $outfile, $save_and_print );
	}

	/**
	 * Generate Raw data.
	 */
	public static function raw( $text, $outfile = false, $level = QRCDR_ECLEVEL_L, $size = 3, $margin = 4 ) {
		$encoder = QrcdrEncode::factory( $level, $size, $margin );
		return $encoder->encodeRAW( $text, $outfile );
	}
}
