#########################################################################################
# Package        HiPi::Interface::EPaper
# Description  : Control Monochrome EPaper displays
# Copyright    : Copyright (c) 2018 Mark Dootson
# License      : This is free software; you can redistribute it and/or modify it under
#                the same terms as the Perl 5 programming language system itself.
#########################################################################################

package HiPi::Interface::EPaper::DisplayBuffer;

#########################################################################################

use strict;
use warnings;
use parent qw( HiPi::Graphics::DrawingContext );
use Carp;
use HiPi qw( :epaper );
use Try::Tiny;

__PACKAGE__->create_ro_accessors( qw(
    device_height device_width buffers buffer_bytes
    frame_1_bpp  frame_2_bpp
    frame_1_type frame_2_type
    frame_invert
    invert_draw offsetx
    colour_frame black_frame
    ) );

__PACKAGE__->create_accessors( qw( pen rotation ) );

our $VERSION ='0.76';

sub new {
    my( $class, %params) = @_;
    $params{pen} //= EPD_BLACK_PEN;
    $params{rotation} //= EPD_BLACK_PEN;
    $params{frame_invert} = [ $params{frame_1_invert}, $params{frame_2_invert} ];
    my $bytecount = $params{device_height} * ( $params{device_width} + $params{offsetx} );
    $bytecount >>= 3;
   
    
    $params{buffers} = [ [], [] ];
    
    if( $params{frame_1_type} != EPD_FRAME_TYPE_UNUSED ) {
        my $mask = ( $params{frame_1_invert} ) ? 0 : 0xFF;
        my @data = ( $mask ) x $bytecount;
        $params{buffers}->[0] = \@data;
    }
    
    if( $params{frame_2_type} != EPD_FRAME_TYPE_UNUSED ) {
        my $mask = ( $params{frame_2_invert} ) ? 0 : 0xFF;
        my @data = ( $mask ) x $bytecount;
        $params{buffers}->[1] = \@data;
    }
    
    $params{buffer_bytes} = $bytecount;
    
    # colour frame & black frame
    $params{black_frame} = 0;
    $params{colour_frame} = 0;
    
    if( $params{frame_1_type} == EPD_FRAME_TYPE_COLOUR ) {
        $params{colour_frame} = 1;
    } elsif($params{frame_2_type} == EPD_FRAME_TYPE_COLOUR ) {
        $params{colour_frame} = 2;
    }
    
    if( $params{frame_1_type} == EPD_FRAME_TYPE_BLACK ) {
        $params{black_frame} = 1;
    } elsif($params{frame_2_type} == EPD_FRAME_TYPE_BLACK ) {
        $params{black_frame} = 2;
    }
    
    $params{colour_frame} ||= $params{black_frame};
    
    my $self = $class->SUPER::new( %params );
    return $self;
}

sub clear_buffer {
    my ($self, $frame) = @_;
    
    if( $frame ) {
        my $mask = ( $self->frame_invert->[$frame] ) ? 0 : 0xFF;
        for (my $i = 0; $i < @{ $self->buffers->[$frame] }; $i ++) {
            $self->buffers->[$frame]->[$i] = $mask;
        }
    } else {
        for my $frameno ( (0, 1) ) {
            my $buffer = $self->buffers->[$frameno];
            my $mask = ( $self->frame_invert->[$frameno] ) ? 0 : 0xFF;
            for (my $i = 0; $i < @$buffer; $i ++) {
                $buffer->[$i] = $mask;
            }
        }
    }
    return;
}

sub draw_pixel {
    my($self, $x, $y ) = @_;
    
    my $frametype = 0;
    if( $self->pen == EPD_COLOUR_PEN ) {
        $frametype = $self->colour_frame;
    } else {
        $frametype = $self->black_frame;
    }
    return unless $frametype;
    
    my $frame = $frametype - 1;
    
    my $inverted = $self->frame_invert->[$frame];
    
    my $maxX = $self->device_width + $self->offsetx;
    my $adjH = $self->device_height -1;
    my $adjW = $self->device_width  -1;
    
    # rotate and check for bounds
    if ($self->rotation == EPD_ROTATION_0 ) {
        if($x < 0 || $x >= $self->device_width || $y < 0 || $y >= $self->device_height) {
            return;
        }
    } elsif ($self->rotation == EPD_ROTATION_90) {
        if($x < 0 || $x >= $self->device_height || $y < 0 || $y >= $self->device_width) {
          return;
        }
        my $swap = $x;
        $x = $adjW - $y;
        $y = $swap;
    } elsif ($self->rotation == EPD_ROTATION_180) {
        if($x < 0 || $x >= $self->device_width || $y < 0 || $y >= $self->device_height) {
          return;
        }
        $x = $adjW - $x;
        $y = $adjH - $y;
    } elsif ($self->rotation == EPD_ROTATION_270) {
        if($x < 0 || $x >= $self->device_height || $y < 0 || $y >= $self->device_width) {
          return;
        }
        my $swap = $x;
        $x = $y;
        $y = $adjH - $swap;
    }
        
    my $index = ($x + $y * $maxX) >> 3;
    my $shiftbits = $x % 8;
    my $buffer = $self->buffers->[$frame];
    
    my $on = ( $self->pen ) ? 1 : 0;
    
    if($self->pen_inverted) {
        $on = ( $on ) ? 0 : 1;
    }
    
    if( $inverted ) {
        if ($on) {
            $buffer->[$index] |= 0x80 >> $shiftbits;
        } else {
            $buffer->[$index] &= ~(0x80 >> $shiftbits);
        }
    } else {
        if ($on) {
            $buffer->[$index] &= ~(0x80 >> $shiftbits);
        } else {
            $buffer->[$index] |= 0x80 >> $shiftbits;
        }
    }
    
    return;
}

sub set_pen {
    my($self, $newpen) = @_;
    my $oldpen = $self->pen;
    $self->pen( $newpen );
    return $oldpen;
}

sub logical_width {
    my $self = shift;
    if( $self->rotation == EPD_ROTATION_90 || $self->rotation == EPD_ROTATION_270 ) {
        return $self->device_height;
    } else {
        return $self->device_width;
    }
}

sub logical_height {
    my $self = shift;
    if( $self->rotation == EPD_ROTATION_90 || $self->rotation == EPD_ROTATION_270 ) {
        return $self->device_width;
    } else {
        return $self->device_height;
    }
}

# noops for buffer contexts

sub rotate { carp q(you cannot call 'rotate' on a display context); }

sub rotated_text { carp q(you cannot call 'rotate_text' on a display context); }

sub clear_context { carp q(you cannot call 'clear_context' on a display context); }


#########################################################################################

package HiPi::Interface::EPaper::PartialContext;

#########################################################################################

use base qw( HiPi::Interface::EPaper::DisplayBuffer );

sub new {
    my($class, %params) = @_;
    my $self = $class->SUPER::new( %params );
    return $self;
}

1;

__END__