/*
 * © 2017 AgNO3 Gmbh & Co. KG
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
package jcifs.smb;


import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import jcifs.CIFSException;
import jcifs.FileNotifyInformation;
import jcifs.SmbConstants;
import jcifs.SmbWatchHandle;
import jcifs.internal.CommonServerMessageBlockRequest;
import jcifs.internal.NotifyResponse;
import jcifs.internal.smb1.trans.nt.NtTransNotifyChange;
import jcifs.internal.smb1.trans.nt.NtTransNotifyChangeResponse;
import jcifs.internal.smb2.notify.Smb2ChangeNotifyRequest;


/**
 * @author mbechler
 *
 */
class SmbWatchHandleImpl implements SmbWatchHandle {

    private static final Logger log = LoggerFactory.getLogger(SmbWatchHandleImpl.class);

    private final SmbFileHandleImpl handle;
    private final int filter;
    private final boolean recursive;
    private AtomicReference<CommonServerMessageBlockRequest> lastRequest = new AtomicReference<>();


    /**
     * @param fh
     * @param filter
     * @param recursive
     * 
     */
    public SmbWatchHandleImpl ( SmbFileHandleImpl fh, int filter, boolean recursive ) {
        this.handle = fh;
        this.filter = filter;
        this.recursive = recursive;
    }


    /**
     * {@inheritDoc}
     *
     * @see jcifs.SmbWatchHandle#watch()
     */
    @Override
    public List<FileNotifyInformation> watch () throws CIFSException {
        if ( !this.handle.isValid() ) {
            throw new SmbException("Watch was broken by tree disconnect");
        }
        try ( SmbTreeHandleImpl th = this.handle.getTree() ) {

            CommonServerMessageBlockRequest req;
            NotifyResponse resp = null;
            if ( th.isSMB2() ) {
                Smb2ChangeNotifyRequest r = new Smb2ChangeNotifyRequest(th.getConfig(), this.handle.getFileId());
                r.setCompletionFilter(this.filter);
                r.setNotifyFlags(this.recursive ? Smb2ChangeNotifyRequest.SMB2_WATCH_TREE : 0);
                req = r;
            }
            else {
                if ( !th.hasCapability(SmbConstants.CAP_NT_SMBS) ) {
                    throw new SmbUnsupportedOperationException("Not supported without CAP_NT_SMBS");
                }

                /*
                 * NtTrans Notify Change Request / Response
                 */
                req = new NtTransNotifyChange(th.getConfig(), this.handle.getFid(), this.filter, this.recursive);
                resp = new NtTransNotifyChangeResponse(th.getConfig());
            }

            if ( log.isTraceEnabled() ) {
                log.trace("Sending NtTransNotifyChange for " + this.handle);
            }
            this.lastRequest.set(req);
            try {
                resp = th.send(req, resp, RequestParam.NO_TIMEOUT, RequestParam.NO_RETRY);
            }
            catch ( SmbException e ) {
                if ( e.getNtStatus() == 0xC0000120 ) {
                    // cancelled
                    log.debug("Request was cancelled", e);
                    return null;
                }
                throw e;
            }
            finally {
                this.lastRequest.set(null);
            }
            if ( log.isTraceEnabled() ) {
                log.trace("Returned from NtTransNotifyChange " + resp.getErrorCode());
            }

            if ( !resp.isReceived() ) {
                throw new CIFSException("Did not receive response");
            }

            if ( resp.getErrorCode() == 0x10B ) {
                this.handle.markClosed();
            }
            if ( resp.getErrorCode() == 0x10C ) {
                resp.getNotifyInformation().clear();
            }
            return resp.getNotifyInformation();
        }
    }


    /**
     * {@inheritDoc}
     *
     * @see jcifs.SmbWatchHandle#call()
     */
    @Override
    public List<FileNotifyInformation> call () throws CIFSException {
        return watch();
    }


    /**
     * {@inheritDoc}
     *
     * @see jcifs.SmbWatchHandle#close()
     */
    @Override
    public void close () throws CIFSException {
        if ( this.handle.isValid() ) {
            try {
                CommonServerMessageBlockRequest lastReq = this.lastRequest.getAndSet(null);
                if ( lastReq != null && !lastReq.getResponse().isReceived() ) {
                    CommonServerMessageBlockRequest cancel = lastReq.createCancel();
                    if ( cancel != null ) {
                        try ( SmbTreeHandleImpl th = this.handle.getTree() ) {
                            th.send(cancel, null);
                        }
                        catch ( SmbException e ) {
                            log.debug("Cancel failed", e);
                        }
                    }
                }
            }
            finally {
                this.handle.close(0L);
            }
        }
    }
}

@@ -819,60 +819,8 @@
 ist;
-%0Aimport java.util.concurrent.atomic.AtomicReference;
 %0A%0Aim
@@ -1561,108 +1561,8 @@
 ve;%0A
-    private AtomicReference%3CCommonServerMessageBlockRequest%3E lastRequest = new AtomicReference%3C%3E();%0A
 %0A%0A  
@@ -3260,47 +3260,8 @@
   %7D%0A
-            this.lastRequest.set(req);%0A
     
@@ -3659,88 +3659,8 @@
   %7D%0A
-            finally %7B%0A                this.lastRequest.set(null);%0A            %7D%0A
     
@@ -4602,699 +4602,8 @@
 ) %7B%0A
-            try %7B%0A                CommonServerMessageBlockRequest lastReq = this.lastRequest.getAndSet(null);%0A                if ( lastReq != null && !lastReq.getResponse().isReceived() ) %7B%0A                    CommonServerMessageBlockRequest cancel = lastReq.createCancel();%0A                    if ( cancel != null ) %7B%0A                        try ( SmbTreeHandleImpl th = this.handle.getTree() ) %7B%0A                            th.send(cancel, null);%0A                        %7D%0A                        catch ( SmbException e ) %7B%0A                            log.debug(%22Cancel failed%22, e);%0A                        %7D%0A                    %7D%0A                %7D%0A            %7D%0A            finally %7B%0A    
     
@@ -4633,30 +4633,16 @@
 se(0L);%0A
-            %7D%0A
         

Remove explicit cancel in watch, no longer necessary as we now actually get the response (wasnt samba's fault at all).