This library is built on [alp-lib](https://www.npmjs.com/package/alp-lib) and implements higher level methods to
parse and build [DASH7](http://www.dash7-alliance.org/) ALP packets.

<a name=Summary></a>
- [Parsing](#Parsing)
    - [alp-lib overlay](#AL.overlay)
        - [parse](#parse)
        - [parsex](#parsex)
    - [Human readable feedback](#Hreadable)
        - [xplain](#xplain)
        - [parse.xplain](#parse.xplain)
- [Building](#Building)
    - [PacketBuilder](#PacketBuilder)
    - [PacketBuilder.Packet](#Packet)
    - [PacketBuilder.LazyPacket](#LazyPacket)
- [Examples](#Examples)
    - [Parsing action](#ExampleParsing)
    - [Common ALP actions building](#ExampleCommand)
    - [Difference between Packet and LazyPacket](#ExamplePacketLazy)

<a name=Parsing>Parsing</a>
======================================================================================================
### <a name=AL.overlay>[alp-lib](https://www.npmjs.com/package/alp-lib) overlay</a>
#### <a name=parse>`parse(payload)`</a>: `any[]`
Shortcut for [alp-lib](https://www.npmjs.com/package/alp-lib) parse.
Parses a binary ALP payload into an array of ALP actions.
- `payload (Buffer | number[])` : The payload to parse.

#### <a name=parsex>`parsex(payload)`</a>: `any[]`
Same as parse, except it takes an hexadecimal string input.
* `payload (Buffer | number[])` : The payload to parse as an hexadecimal string.

### <a name=Hreadable>Human readable feedback</a>
#### <a name=xplain>`xplain(packets)`</a>: `any[]`
Turns an ALP parsed packet array into a more human readable version.
- `packets (any[])`: Array of ALP packets (output of [`parse`](#parse)).

#### <a name=parse.xplain>`parse.xplain(payload)`</a>: `any[]`
Parses a ALP payload directly into a human readable decoded structure.
- `payload (Buffer | number[])` : The payload to parse.

<a name=Building>Building</a>
======================================================================================================
### class <a name=PacketBuilder>PacketBuilder</a>
Creates a packet builder capable of creating [Packet](#Packet)(s).

#### `Constructor()`
#### `set_default_params(partial_default_params)`
- `partial_default_params`: Hash map containing: { `"action_name"`: `<default_action_parameters>` }
When this function is called, the PacketBuilder completes its default actions parameters by saving the specified
actions parameters as the new defaults. This doesn't erase the default parameters of missing actions in the
`partial_default_params` hash map. For each action specified, the defaults parameters are completely replaced by
the new default parameter specification.

Any [PacketBuilder.Packet](#Packet) or [PacketBuilder.LazyPacket](#LazyPacket) created with this PacketBuilder will
use those default actions parameters.

#### `new()`: [PacketBuilder.Packet](#Packet)
Creates a new Packet.

#### `lazy()`: [PacketBuilder.LazyPacket](#LazyPacket)
Creates a new Lazy Packet.

#### [Packet](#Packet) "like"
The PacketBuilder implements all the [action methods](#SpecBuildMethods) as a shortcut for:
`builder.new().<action>()`.

### class <a name=Packet>PacketBuilder.Packet</a>
Byte array representing an ALP packet containing a list of ALP actions. Each of its ALP action related method
creates a new Packet containing this packet's ALP actions followed by the method's ALP action.

#### Standard methods
##### `buffer()`: `Buffer`
Builds the ALP packet as a NodeJS Buffer.

##### `packet_builder()`: [PacketBuilder](#PacketBuilder)
Creates a PacketBuilder that uses this packet as header for its produced packets.

##### `xplain()`: `any[]`
Gives an [`xplain`](#xplain) representation of this packet.

#### <a name=SpecBuildMethods>Specification dependent methods</a>
All those methods return the [Packet](#Packet) object therefore allowing building a packet by chaining the methods.
<div name=PACKET_BUILDER_SPEC><div>

### class <a name=LazyPacket>PacketBuilder.LazyPacket</a>
Unlike the [PacketBuilder.Packet](#Packet) class, this class ALP action methods do not trigger a binary packet
build right away. Instead they add the ALP packet corresponding to the call to an internal representation that can be built later.

Therefore, the ALP action methods return the original packet itself instead of a new packet (as opposed to the
[PacketBuilder.Packet](#Packet) methods behavior).

See [examples](#ExamplePacketLazy) for more details on the different behaviors between the packet classes.

#### Standard methods
##### `build()`: [PacketBuilder.Packet](#Packet)
Builds an ALP packet.

##### `build_buffer()`: `Buffer`
Builds the current ALP packet into a buffer.

##### `xplain()`: `any[]`
Gives an [`xplain`](#xplain) representation of this packet.

##### `list()`: `any[]`
Return an Object representation of the contained ALP actions that can be passed the `alp-lib.build` to build the
corresponding binary packet.

#### Specification dependent methods
Implements all the [specification dependent PacketBuilder.Packet methods](#SpecBuildMethods) but in a "lazy" way.

<a name=Examples>Examples</a>
======================================================================================================
### <a name=ExampleParsing>Parsing action </a>
```
const AT = require("../alp_tools");

const read_command_packet = Buffer.from("B45641420007")
console.log("parse", AT.parse(binary));
console.log("parse.xplain", AT.parse.xplain(binary));

let complex_payload = new Buffer("B400F3640004000841010142004142000941420001", "hex");
console.log("Complex payload explained:", AT.parse.xplain(complex_payload));

```

### <a name=ExampleCommand>Common ALP actions building</a>
```
const AT = require("./alp_tools");

// Print helper
function bin_pkt_print(first_line, binary, no_xplain) {
    console.log(first_line);
    console.log(binary.buffer());
    // Comparing the standard parse and xplain parse versions
    console.log("PARSE", AT.parse(binary));
    console.log("PARSE.XPLAIN", AT.parse.xplain(binary));
    console.log();
}

// Creating a packet builder
let alp_builder = new AT.PacketBuilder();

// Building a status
let status = alp_builder.status({
    ext: 0,
    action_id: 1,
    status: 2,
});
bin_pkt_print("STATUS:", status, true);

// Building a local read file
let local_read_file = alp_builder
    .tag({
        eop: true,
        tag_id: 4,
    }).read_file({
        resp: true,
        group: false,

        file_id: 0, // UID file
        file_offset: 0,
        length: 8,
    });
bin_pkt_print("LOCAL_READ_FILE:", local_read_file, true);

// Setting some common default parameters for the ALP builder for ergonomy
alp_builder.set_default_params({
    tag: {eop: true, tag_id: 0},
    read_file: {resp: true, group: false, file_offset: 0},
    write_file: {resp: true, group: false, file_offset: 0},
    fwd: {
        resp: true,
        group: false,

        itf_id: 0xD7,

        stop_on_err: false,
        record: false,
        retry_mode: 0,
        resp_mode: 1,
        dorm_to: 0,
        te: 0,

        addr: {
            addr_type: 2,
            nls_method: 6,
            estimated_reached: 1,
        },
    },
});

// Building the local read more easily
let easy_local_read_file = alp_builder
    .tag()
    .read_file({
        file_id: 0, // UID file
        length: 8,
    });
bin_pkt_print("EASY_LOCAL_READ_FILE:", easy_local_read_file, true);

// Building a distant write file
let distant_write_file = alp_builder
    .tag()
    .fwd({
        addr: {
            id: Buffer.from("001BC50C70000D11", "hex"),
            access_class: 0x01,
        },
    })
    .write_file({
        file_id: 0x42, // scratch file on devices modem
        data: Buffer.from("FEDCBA", "hex"),
    });

// Unfortunately, as can be seen in the next print, AT.parse.xplain still have trouble explaining some action
    // fields.
bin_pkt_print("DISTANT_WRITE_FILE:", distant_write_file, true);
```

### <a name=ExamplePacketLazy>Difference between [Packet](#Packet) and [LazyPacket](#LazyPacket)</a>
```
const AT = require("../alp_tools");

// Creating a packet builder
let alp_builder = new AT.PacketBuilder();

// Setting some common default parameters for the ALP builder for ergonomy
alp_builder.set_default_params({
    tag: {eop: true, tag_id: 0},
    read_file: {resp: true, group: false, file_offset: 0},
    write_file: {resp: true, group: false, file_offset: 0},
    fwd: {
        resp: true,
        group: false,

        itf_id: 0xD7,

        stop_on_err: false,
        record: false,
        retry_mode: 0,
        resp_mode: 1,
        dorm_to: 0,
        te: 0,

        addr: {
            addr_type: 2,
            nls_method: 6,
            estimated_reached: 1,
        },
    },
});

// Test function
function test_packet(packet) {
    let tag = packet.tag();
    let tagged_read = tag.read_file({file_id: 0, length: 5});

    console.log("Packet tag: ", tag.toString());
    console.log("Packet tagged_read", tagged_read.toString());
}

// Test Packet
console.log("Testing Packet");
test_packet(alp_builder.new());
console.log();

// Test LazyPacket
console.log("Testing LazyPacket");
test_packet(alp_builder.lazy());
console.log("Oh no! Packet tag contains a read!");
console.log();

// Correct LazyPacket usage
let packet = alp_builder.lazy();
packet.tag();
for(let i = 0; i < 10; ++i) {
    packet.read_file({file_id: i, length: i});
}
console.log("LazyPacket many reads:", packet.toString());
```
