#########################################################################################
# Package        HiPi::Interface::ZeroSeg
# Description  : Interface to Pi Hut ZeroSeg pHAT
# Copyright    : Copyright (c) 2018-2023 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::ZeroSeg;

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

use strict;
use warnings;
use parent qw( HiPi::Interface );
use HiPi qw( :max7219 );
use HiPi::Interface::MAX7219;

our $VERSION ='0.89';

__PACKAGE__->create_accessors( qw( buffer writeflags flipped _decimals _shutdown_on_exit segmentfont ) );

my $defaultsegmentfont = {
    ' ' => 0x00,
    '-' => 0x01,
	'_' => 0x08,
    '>' => 0b00000111,
    '<' => 0b00110001,
    '=' => 0b00001001,
    '0' => 0x7e,
    '1' => 0x30,
    '2' => 0x6d,
    '3' => 0x79,
    '4' => 0x33,
    '5' => 0x5b,
    '6' => 0x5f,
    '7' => 0x70,
    '8' => 0x7f,
    '9' => 0x7b,
	'a' => 0x7d,
    'b' => 0x1f,
	'c' => 0x0d,
    'd' => 0x3d,
	'e' => 0x6f,
	'f' => 0x47,
	'g' => 0x7b,
	'h' => 0x17,
	'i' => 0x10,
	'j' => 0x18,
	'k' => 0b00000111,
	'l' => 0x06,
	'm' => 0b01000001,
	'n' => 0x15,
	'o' => 0x1d,
	'p' => 0x67,
	'q' => 0x73,
	'r' => 0x05,
	's' => 0x5b,
	't' => 0x0f,
	'u' => 0x1c,
	'v' => 0x1c,
	'w' => 0b00010100,
	'x' => 0b00100101,
	'y' => 0x3b,
	'z' => 0x6d,
	'A' => 0x77,
	'B' => 0x7f,
	'C' => 0x4e,
	'D' => 0x7e,
	'E' => 0x4f,
	'F' => 0x47,
	'G' => 0x5e,
	'H' => 0x37,
	'I' => 0x30,
	'J' => 0x38,
	'K' => 0b00110001,
	'L' => 0x0e,
	'M' => 0b01001001,
	'N' => 0x76,
	'O' => 0b01100011,
	'P' => 0x67,
	'Q' => 0x73,
	'R' => 0x46,
	'S' => 0x5b,
	'T' => 0x0f,
	'U' => 0x3e,
	'V' => 0x3e,
	'W' => 0b00110110,
	'X' => 0b00010011,
	'Y' => 0x3b,
	'Z' => 0x6d,
	',' => 0x80,
	'.' => 0x80,
};

sub new {
    my ($class, %userparams) = @_;
    
    my %params = (
        devicename   => '/dev/spidev0.0',
        speed        => 8000000,  # 8 mhz
        delay        => 0,
        _shutdown_on_exit => 1,
        segmentfont  => $defaultsegmentfont,
    );
    
    # get user params
    foreach my $key( keys (%userparams) ) {
        $params{$key} = $userparams{$key};
    }
    
    $params{buffer} = [];
    $params{_decimals} = [0,0,0,0,0,0,0,0];
        
    unless(defined($params{device})) {
        $params{device} = HiPi::Interface::MAX7219->new(
            speed        => $params{speed},
            delay        => $params{delay},
            devicename   => $params{devicename},
        );
    }
    
    my $self = $class->SUPER::new(%params);
    HiPi->register_exit_method( $self, '_on_exit');
    $self->device->set_decode_mode( 0 );
    $self->device->set_scan_limit( 7 );
    $self->device->set_intensity( 2 );
    $self->device->wake_up;
    $self->device->set_display_test(0);
    
    return $self;
}

sub set_shutdown_on_exit {
    my ($self, $state) = @_;
    $state = ( $state ) ? 1 : 0;
    $self->_shutdown_on_exit( $state );
}

sub _on_exit {
    my $self = shift;
    if(  $self->_shutdown_on_exit ) {
        $self->device->shutdown;
    }
}

sub set_buffer_text {
    my( $self, $text ) = @_;
    
    # padleft
    $text = sprintf('%8s', $text);
    
    my @chars = split(//, $text);
    
    $self->buffer(\@chars );
    $self->_decimals( [0,0,0,0,0,0,0,0] );
    return $text;
}

sub write_decimal_number {
    my ($self, $value) = @_;
    $value =~ s/\s+//g;
    
    my $dp = index($value, '.');
    
    $value =~ s/\.//g;
    $self->_decimals( [0,0,0,0,0,0,0,0] );
    
    if($dp > -1 ) {
        if( length( $value ) < 8 ) {
            $dp += ( 8 - length( $value ) );
        }
        $self->_decimals->[$dp - 1] = 1 if $dp > 0;
    }
    
    my @chars = split(//, sprintf('%8s', $value) );
    $self->buffer(\@chars );
    $self->write_buffer;
    return $value;
}

sub write_degrees {
    my ($self, $value, $scale) = @_;
    $value =~ s/\s+//g;
    $scale //='';
    if($scale) {
        $scale =~ s/[^fc]//ig;
    }
    
    $value .= 'O' . uc($scale);
    
    my $dp = index($value, '.');
    
    $value =~ s/\.//g;
    $self->_decimals( [0,0,0,0,0,0,0,0] );
    
    if($dp > -1 ) {
        if( length( $value ) < 8 ) {
            $dp += ( 8 - length( $value ) );
        }
        $self->_decimals->[$dp - 1] = 1 if $dp > 0;
    }
    
    my @chars = split(//, sprintf('%8s', $value) );
    $self->buffer(\@chars );
    $self->write_buffer;
    return $value;
}

sub write_time {
    my ( $self, $hour, $minute, $second ) = @_;
    $hour ||= 0;
    $minute ||= 0;
    
    for ( $hour, $minute, $second) {
        $_ = sprintf("%02d", $_) if defined( $_ );
    }
    my $timestring = $hour . $minute;
    if(defined($second)) {
        $timestring .= $second;
        $self->_decimals( [0,0,0,1,0,1,0,0] );
    } else {
        $self->_decimals( [0,0,0,0,0,1,0,0] );
    }
    my @chars = split(//, sprintf('%8s', $timestring) );
    $self->buffer(\@chars );
    $self->write_buffer;
    return $timestring;
}

sub write_localtime {
    my ( $self, $skipseconds ) = @_;
    my ($second, $minute, $hour ) = localtime(time);
    $second = ( $skipseconds ) ? undef : $second;
    my $rval =  $self->write_time( $hour, $minute, $second);
    return $rval;
}

sub write_gmtime {
    my ( $self, $skipseconds ) = @_;
    my ($second, $minute, $hour ) = gmtime(time);
    $second = ( $skipseconds ) ? undef : $second;
    my $rval = $self->write_time( $hour, $minute, $second);
    return $rval;
}

sub write_buffer {
    my $self = shift;
    for (my $i = 0; $i < 8; $i ++) {
        my $flags = 0;
        $flags |= MAX7219_FLAG_DECIMAL if $self->_decimals->[$i];
        $self->_write_segment_char( 7 - $i , $self->buffer->[$i], $flags );
    }
    return;
}

sub write_text {
    my( $self, $text ) = @_;
    $self->set_buffer_text($text);
    $self->write_buffer;
}

sub write_raw_bytes {
    my( $self, @bytes ) = @_;
    while( scalar(@bytes) < 8 ) {
        unshift( @bytes, 0x00 );
    }
    
    for (my $i = 0; $i < 8; $i ++) {
        $self->device->send_segment_matrix( 7 - $i, $bytes[$i] );
        $self->sleep_milliseconds(10);
    }
    
    return;
}

sub scroll_buffer_left {
    my $self = shift;
    if( scalar @{ $self->buffer } ) {
        push @{ $self->buffer }, shift @{ $self->buffer };
    }
    return;
}

sub scroll_buffer_right {
    my $self = shift;
    if( scalar @{ $self->buffer } ) {
        unshift @{ $self->buffer }, pop @{ $self->buffer };
    }
    return;
}

sub clear_display {
    my $self = shift;
    my $text = ' ' x 8;
    $self->write_text($text);
    return;
}

sub shut_down { $_[0]->device->shutdown; }

sub wake_up { $_[0]->device->wake_up; }

sub set_intensity{ $_[0]->device->set_intensity( $_[1] ); }

sub _write_segment_char {
    my($self, $matrix, $char, $flags ) = @_;
    $flags //= 0x00;
    $char  //= '_';
    my $byte = ( exists($self->segmentfont->{$char}) ) ? $self->segmentfont->{$char} : 0x08;
    
    if( $flags & MAX7219_FLAG_FLIPPED ) {
        $byte = (($byte & 0xE) << 3) | (($byte & 0x70) >> 3) | ( $byte & 0x80 ) | ( $byte & 0x01 );
    }
    
    if( $flags & MAX7219_FLAG_DECIMAL ) {
        $byte |= 0x80;
    }
    
    if( $flags & MAX7219_FLAG_MIRROR ) {
        # swap bits 5 and 1
        # swap bits 4 and 2
        for my $swap ( [ 5, 1], [ 4, 2 ] ) {
            my $val0 =  ( $byte >> $swap->[0] ) & 1;
            my $val1 =  ( $byte >> $swap->[1] ) & 1;
            
            if( $val0 ) {
                $byte |= ( 1 << $swap->[1] ); 
            } else {
                $byte &= ~( 1 << $swap->[1] );
            }
            
            if( $val1 ) {
                $byte |= ( 1 << $swap->[0] ); 
            } else {
                $byte &= ~( 1 << $swap->[0] );
            }
        } 
    }
        
    $self->device->send_segment_matrix( $matrix, $byte );
    $self->sleep_milliseconds(10);
}


1;

__END__