#########################################################################################
# Package        HiPi::Interface::MAX7219
# Description  : Interface to MAX7219 matrix LED driver
# 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::MAX7219;

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

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

our $VERSION ='0.89';

__PACKAGE__->create_accessors( qw( ) );

my $codefont = {
    '0' => 0b0000,
    '1' => 0b0001,
    '2' => 0b0010,
    '3' => 0b0011,
    '4' => 0b0100,
    '5' => 0b0101,
    '6' => 0b0110,
    '7' => 0b0111,
    '8' => 0b1000,
    '9' => 0b1001,
    '-' => 0b1010,
    'E' => 0b1011,
    'H' => 0b1100,
    'L' => 0b1101,
    'P' => 0b1110,
    ' ' => 0b1111,
};

sub _get_code_char {
    my $char = shift;
    return ( exists($codefont->{$char}) ) ? $codefont->{$char} : $codefont->{' '};
}

sub new {
    my ($class, %userparams) = @_;
    
    my %params = (
        devicename   => '/dev/spidev0.0',
        speed        => 8000000,  # 8 mhz
        delay        => 0,
    );
    
    # get user params
    foreach my $key( keys (%userparams) ) {
        $params{$key} = $userparams{$key};
    }
    
    unless(defined($params{device})) {
        $params{device} = HiPi::Device::SPI->new(
            speed        => $params{speed},
            delay        => $params{delay},
            devicename   => $params{devicename},
        );
    }
    
    my $self = $class->SUPER::new(%params);
    
    $self->set_display_test( 0 );
    
    return $self;
}

sub write_code_char {
    my($self, $matrix, $char, $flags, $cascade) = @_;
    $flags //= 0x00;
    $char  //= ' ';
    my $byte = _get_code_char( $char );
    
    if( $flags & MAX7219_FLAG_DECIMAL ) {
        $byte |= 0x80;
    }
    
    $self->send_segment_matrix( $matrix, $byte, $cascade );
    $self->sleep_milliseconds(10);
}

sub send_segment_matrix {
    # send data for single matrix
    my($self, $matrix, $byte, $cascade) = @_;
    return unless($matrix >= 0 && $matrix < 8 );
    my $reg = MAX7219_REG_DIGIT_0 + $matrix;
    $self->send_command( $reg, $byte, $cascade );
}

sub send_command {
    my($self, $register, $data, $cascade ) = @_;
    $cascade ||= 0;
    my @bytes = ( $register, $data );
    if( $cascade ) {
        for (my $i = 0; $i < $cascade; $i ++ ) {
            push( @bytes, MAX7219_REG_NOOP, 0x00 );
        }
    }
    $self->device->transfer_byte_array( @bytes );
    return;
}

sub send_raw_bytes {
    my($self, @bytes) = @_;
    $self->device->transfer_byte_array( @bytes );
    return;
}

sub set_decode_mode {
    my($self, $mode, $cascade ) = @_;
    # only covers all on or all off
    # see data sheet for mixed settings
    $mode = ( $mode ) ? 0xFF : 0x00;
    $self->send_command( MAX7219_REG_DECODE_MODE, $mode, $cascade );
    return;
}

sub set_intensity {
    my($self, $value, $cascade ) = @_;
    # value between 0 ( min ) and 15  ( max )
    $value &= 0xF; 
    $self->send_command( MAX7219_REG_INTENSITY, $value, $cascade );
    return;
}

sub set_scan_limit {
    my($self,  $value, $cascade ) = @_;
    # value between 0 and 7 - how many registers are scanned
    $value &= 0x7;
    $self->send_command( MAX7219_REG_SCAN_LIMIT, $value, $cascade );
    return;
}

sub shutdown {
    my($self, $cascade ) = @_;
    $self->send_command( MAX7219_REG_SHUTDOWN, 0x00, $cascade );
    return;
}

sub wake_up {
    my($self, $cascade ) = @_;
    $self->send_command( MAX7219_REG_SHUTDOWN, 0x01, $cascade );
    return;
}

sub set_display_test {
    my( $self, $testmode, $cascade ) = @_;
    $testmode = ( $testmode ) ? 0x01 : 0x00;
    $self->send_command( MAX7219_REG_TEST, $testmode, $cascade );
    $self->sleep_milliseconds(10);
}

1;

__END__